Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62a3d8c834 | |||
| fd2afa9ec6 | |||
| 949e9faef3 | |||
| 5df84d6017 | |||
| 08c85129f3 | |||
| a980740fd5 | |||
| 338b4d61b4 | |||
| 25b832a6e9 |
@@ -1,24 +0,0 @@
|
|||||||
name: Test And Build
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
Lint:
|
|
||||||
runs-on: linepics
|
|
||||||
steps:
|
|
||||||
- name: checkout repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: cppcheck
|
|
||||||
run: cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
|
|
||||||
- name: formatting
|
|
||||||
run: clang-format --style=file --Werror --dry-run src/*.cpp
|
|
||||||
Build:
|
|
||||||
runs-on: linepics
|
|
||||||
steps:
|
|
||||||
- name: checkout repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: 'true'
|
|
||||||
- run: |
|
|
||||||
sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
|
|
||||||
echo -e "\nIGNORE_SUBMODULES += sinqmotor" >> Makefile
|
|
||||||
make install
|
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,8 +0,0 @@
|
|||||||
O.*
|
|
||||||
.cvsignore
|
|
||||||
.vscode
|
|
||||||
src/.vscode
|
|
||||||
utils/analyzeTcpDump/__pycache__
|
|
||||||
utils/analyzeTcpDump/demo.pcap.json
|
|
||||||
utils/analyzeTcpDump/demovenv
|
|
||||||
utils/analyzeTcpDump/venv
|
|
||||||
58
.gitlab-ci.yml
Normal file
58
.gitlab-ci.yml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
default:
|
||||||
|
image: docker.psi.ch:5000/sinqdev/sinqepics:latest
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- lint
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
|
||||||
|
cppcheck:
|
||||||
|
stage: lint
|
||||||
|
script:
|
||||||
|
- cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
|
||||||
|
artifacts:
|
||||||
|
expire_in: 1 week
|
||||||
|
tags:
|
||||||
|
- sinq
|
||||||
|
|
||||||
|
formatting:
|
||||||
|
stage: lint
|
||||||
|
script:
|
||||||
|
- clang-format --style=file --Werror --dry-run src/*.cpp
|
||||||
|
artifacts:
|
||||||
|
expire_in: 1 week
|
||||||
|
tags:
|
||||||
|
- sinq
|
||||||
|
|
||||||
|
# clangtidy:
|
||||||
|
# stage: lint
|
||||||
|
# script:
|
||||||
|
# - curl https://docker.psi.ch:5000/v2/_catalog
|
||||||
|
# # - dnf update -y
|
||||||
|
# # - dnf install -y clang-tools-extra
|
||||||
|
# # - clang-tidy sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h -checks=cppcoreguidelines-*,cert-*
|
||||||
|
# # tags:
|
||||||
|
# # - sinq
|
||||||
|
|
||||||
|
build_module:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- export SINQMOTOR_VERSION="$(grep 'sinqMotor_VERSION=' Makefile | cut -d= -f2)"
|
||||||
|
- git clone --depth 1 --branch "${SINQMOTOR_VERSION}" https://gitlab-ci-token:${CI_JOB_TOKEN}@git.psi.ch/sinq-epics-modules/sinqmotor.git
|
||||||
|
- pushd sinqmotor
|
||||||
|
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
|
||||||
|
- echo "LIBVERSION=${SINQMOTOR_VERSION}" >> Makefile
|
||||||
|
- make install
|
||||||
|
- popd
|
||||||
|
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
|
||||||
|
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
|
||||||
|
- make install
|
||||||
|
- cp -rT "/ioc/modules/turboPmac/$(ls -U /ioc/modules/turboPmac/ | head -1)" "./turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
|
||||||
|
artifacts:
|
||||||
|
name: "turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
|
||||||
|
paths:
|
||||||
|
- "turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
|
||||||
|
expire_in: 1 week
|
||||||
|
when: always
|
||||||
|
tags:
|
||||||
|
- sinq
|
||||||
3
Makefile
3
Makefile
@@ -34,5 +34,4 @@ TEMPLATES += db/turboPmac.db
|
|||||||
DBDS += sinqMotor/src/sinqMotor.dbd
|
DBDS += sinqMotor/src/sinqMotor.dbd
|
||||||
DBDS += src/turboPmac.dbd
|
DBDS += src/turboPmac.dbd
|
||||||
|
|
||||||
USR_CFLAGS += -Wall -Wextra -Wunused-result -Werror -fvisibility=hidden # -Wpedantic // Does not work because EPICS macros trigger warnings
|
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings
|
||||||
USR_CXXFLAGS += -Wall -Wextra -Wunused-result -Werror -fvisibility=hidden
|
|
||||||
46
README.md
46
README.md
@@ -4,26 +4,15 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This is a driver for the Turbo PMAC motion controller with the SINQ
|
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.
|
||||||
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
|
This driver is a standard sinqMotor-derived which however uses a special low level IP Port driver (`pmacAsynIPPortConfigure`) instead of the standard `drvAsynIPPortConfigure`. For the general configuration, please see https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
|
||||||
level IP Port driver (`pmacAsynIPPortConfigure`) instead of the standard
|
|
||||||
`drvAsynIPPortConfigure`. For the general configuration, please see
|
|
||||||
https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
|
|
||||||
|
|
||||||
The folder "utils" contains utility scripts for working with pmac motor
|
The folder "utils" contains utility scripts for working with pmac motor controllers. To read their manual, run the scripts without any arguments.
|
||||||
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.
|
||||||
- writeRead.py: Allows sending commands to and receiving commands from a pmac
|
- 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.
|
||||||
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
|
||||||
|
|
||||||
@@ -33,15 +22,13 @@ 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
|
# 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.
|
||||||
# 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
|
||||||
# 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")
|
||||||
|
|
||||||
@@ -61,8 +48,7 @@ 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
|
# Configure the timeout frequency watchdog: A maximum of 10 timeouts are allowed in 300 seconds before an alarm message is sent.
|
||||||
# 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.
|
||||||
@@ -75,22 +61,14 @@ dbLoadRecords("$(turboPmac_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(I
|
|||||||
|
|
||||||
### Additional records
|
### Additional records
|
||||||
|
|
||||||
`turboPmac` provides a variety of additional records. See `db/turboPmac.db` for
|
`turboPmac` provides a variety of additional records. See `db/turboPmac.db` for the complete list and the documentation.
|
||||||
the complete list and the documentation.
|
|
||||||
|
|
||||||
## Developer guide
|
## Developer guide
|
||||||
|
|
||||||
### Versioning
|
### Versioning
|
||||||
|
|
||||||
Please see the documentation for the module sinqMotor:
|
Please see the documentation for the module sinqMotor: https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
|
||||||
https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
|
|
||||||
|
|
||||||
### How to build it
|
### How to build it
|
||||||
|
|
||||||
This driver can be compiled and installed by running `make install` from the
|
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.
|
||||||
same directory where the Makefile is located. However, since it uses the git
|
|
||||||
submodule sinqMotor, make sure that the correct version of the submodule
|
|
||||||
repository is checked out AND the change is commited (`git status` shows no
|
|
||||||
non-committed changes). Please see the section "Usage as static dependency" in
|
|
||||||
https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md for
|
|
||||||
more details.
|
|
||||||
|
|||||||
Binary file not shown.
Submodule sinqMotor updated: 97c3ed4556...c65a8de5dd
@@ -230,7 +230,8 @@ static asynStatus sendPmacGetBuffer(pmacPvt *pPmacPvt, asynUser *pasynUser,
|
|||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
int pmacAsynIPPortConfigure(const char *portName, const char *hostInfo) {
|
epicsShareFunc int pmacAsynIPPortConfigure(const char *portName,
|
||||||
|
const char *hostInfo) {
|
||||||
asynStatus status = asynSuccess;
|
asynStatus status = asynSuccess;
|
||||||
asynInterface *int32LowerLevelInterface = NULL;
|
asynInterface *int32LowerLevelInterface = NULL;
|
||||||
asynInterface *octetLowerLevelInterface = NULL;
|
asynInterface *octetLowerLevelInterface = NULL;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#define asynInterposePmac_H
|
#define asynInterposePmac_H
|
||||||
|
|
||||||
#include <epicsExport.h>
|
#include <epicsExport.h>
|
||||||
#include <macros.h>
|
|
||||||
#include <shareLib.h>
|
#include <shareLib.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -26,7 +25,8 @@ extern "C" {
|
|||||||
* 172.23.243.156:1025)
|
* 172.23.243.156:1025)
|
||||||
* @return status
|
* @return status
|
||||||
*/
|
*/
|
||||||
int HIDDEN pmacAsynIPPortConfigure(const char *portName, const char *hostInfo);
|
epicsShareFunc int pmacAsynIPPortConfigure(const char *portName,
|
||||||
|
const char *hostInfo);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,13 +70,12 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo,
|
|||||||
axes.push_back(this);
|
axes.push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
pTurboPmacA_ = std::make_unique<turboPmacAxisImpl>((turboPmacAxisImpl){
|
pTurboPmacA_ = std::make_unique<turboPmacAxisImpl>(
|
||||||
.waitForHandshake = false,
|
(turboPmacAxisImpl){.waitForHandshake = false,
|
||||||
.enableDisable = false,
|
.enableDisable = false,
|
||||||
.timeAtHandshake = 0,
|
.timeAtHandshake = 0,
|
||||||
.axisStatus = 0,
|
.axisStatus = 0,
|
||||||
.needInit = false,
|
.needInit = false});
|
||||||
});
|
|
||||||
|
|
||||||
// Provide initial values for some parameter library entries
|
// Provide initial values for some parameter library entries
|
||||||
status = pC_->setIntegerParam(axisNo_, pC_->rereadEncoderPosition(), 0);
|
status = pC_->setIntegerParam(axisNo_, pC_->rereadEncoderPosition(), 0);
|
||||||
@@ -133,16 +132,9 @@ asynStatus turboPmacAxis::init() {
|
|||||||
double motorVelocity = 0.0;
|
double motorVelocity = 0.0;
|
||||||
double motorVmax = 0.0;
|
double motorVmax = 0.0;
|
||||||
double motorAccel = 0.0;
|
double motorAccel = 0.0;
|
||||||
|
int acoDelay = 0.0; // Offset time for the movement watchdog caused by
|
||||||
// Conversion factor between engineering units (EU) and encoder counts
|
// the air cushions in milliseconds.
|
||||||
double counts_to_eu = 0.0;
|
int axStatus = 0;
|
||||||
|
|
||||||
// Deadband in counts. Can be a fraction of a count, hence double.
|
|
||||||
double deadband_counts = 0.0;
|
|
||||||
|
|
||||||
// Offset time for the movement watchdog caused by the air cushions in
|
|
||||||
// milliseconds.
|
|
||||||
int acoDelay = 0.0;
|
|
||||||
|
|
||||||
// The parameter library takes some time to be initialized. Therefore we
|
// The parameter library takes some time to be initialized. Therefore we
|
||||||
// wait until the status is not asynParamUndefined anymore.
|
// wait until the status is not asynParamUndefined anymore.
|
||||||
@@ -171,46 +163,27 @@ asynStatus turboPmacAxis::init() {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Read out the axis status, the current position, current and maximum speed,
|
Read out the axis status, the current position, current and maximum speed,
|
||||||
acceleration, the air cushion delay, the scaling factor between counts and
|
acceleration and the air cushion delay.
|
||||||
engineering units ([Qxx00] = engineering units / counts) and the deadband
|
|
||||||
Ixx65.
|
|
||||||
*/
|
*/
|
||||||
snprintf(command, sizeof(command),
|
snprintf(command, sizeof(command),
|
||||||
"Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22 Q%2.2d00 I%2.2d65",
|
"P%2.2d00 Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22", axisNo_,
|
||||||
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
|
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
|
||||||
status = pC_->writeRead(axisNo_, command, response, 7);
|
status = pC_->writeRead(axisNo_, command, response, 6);
|
||||||
|
|
||||||
msgPrintControlKey keyDisconnected = msgPrintControlKey(
|
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, 1);
|
|
||||||
|
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(keyDisconnected, true,
|
asynPrint(
|
||||||
pC_->pasynUser())) {
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
asynPrint(
|
"Controller \"%s\", axis %d => %s, line %d\nCould not communicate "
|
||||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
"with controller during IOC initialization. Check if you used "
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nCould not "
|
"\"pmacAsynIPPortConfigure\" instead of the standard "
|
||||||
"communicate with controller during IOC initialization (error "
|
"\"drvAsynIPPortConfigure\" function in the .cmd file in order to "
|
||||||
"%s). Did you use \"drvAsynIPPortConfigure\" instead of "
|
"create the port driver.\n",
|
||||||
"\"pmacAsynIPPortConfigure\" in the .cmd file to create the "
|
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__);
|
||||||
"port driver?\n",
|
|
||||||
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__,
|
|
||||||
pC_->stringifyAsynStatus(status));
|
|
||||||
}
|
|
||||||
pTurboPmacA_->needInit = true;
|
pTurboPmacA_->needInit = true;
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
nvals = sscanf(response, "%d %lf %lf %lf %lf %d", &axStatus, &motorPos,
|
||||||
|
&motorVmax, &motorVelocity, &motorAccel, &acoDelay);
|
||||||
|
|
||||||
// No error has been detected -> Reset the error count
|
// The acoDelay is given in milliseconds -> Convert to seconds, rounded up
|
||||||
if (status == asynSuccess) {
|
|
||||||
pC_->getMsgPrintControl().resetCount(keyDisconnected, pC_->pasynUser());
|
|
||||||
}
|
|
||||||
|
|
||||||
nvals = sscanf(response, "%lf %lf %lf %lf %d %lf %lf", &motorPos,
|
|
||||||
&motorVmax, &motorVelocity, &motorAccel, &acoDelay,
|
|
||||||
&counts_to_eu, &deadband_counts);
|
|
||||||
|
|
||||||
// The acoDelay is given in milliseconds -> Convert to seconds, rounded
|
|
||||||
// up
|
|
||||||
setOffsetMovTimeout(std::ceil(acoDelay / 1000.0));
|
setOffsetMovTimeout(std::ceil(acoDelay / 1000.0));
|
||||||
|
|
||||||
// The PMAC electronic specifies the acceleration in m/s^2. Since we
|
// The PMAC electronic specifies the acceleration in m/s^2. Since we
|
||||||
@@ -218,7 +191,7 @@ asynStatus turboPmacAxis::init() {
|
|||||||
// here to mm/s^2.
|
// here to mm/s^2.
|
||||||
motorAccel = motorAccel * 1000;
|
motorAccel = motorAccel * 1000;
|
||||||
|
|
||||||
if (nvals != 8) {
|
if (nvals != 6) {
|
||||||
return pC_->couldNotParseResponse(command, response, axisNo_,
|
return pC_->couldNotParseResponse(command, response, axisNo_,
|
||||||
__PRETTY_FUNCTION__, __LINE__);
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
}
|
}
|
||||||
@@ -242,38 +215,17 @@ asynStatus turboPmacAxis::init() {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Check if the new target position is within the range current position
|
|
||||||
+/- deadband. If that is the case, no movement command should be sent.
|
|
||||||
This functionality is implemented within the motor record itself, we
|
|
||||||
just need to populate the field SPDP (see record PushSPDB2Field in
|
|
||||||
db/turboPmac.db)
|
|
||||||
|
|
||||||
First, the deadband is read out (Ixx65, see Turbo PMAC User Manual, p.
|
|
||||||
160). This value then needs to be converted into counts (by dividing it
|
|
||||||
by 16). After words, the deadband in counts then need to be converted
|
|
||||||
into engineering units. For this, the scaling factor Qxx10 needs to be
|
|
||||||
applied (see SINQ_2G_MCU_SW_manual_rev7, p. 17): [Qxx10] = engineering
|
|
||||||
units (eu) / counts deadband_eu = Qxx10 * Ixx65 / 16
|
|
||||||
|
|
||||||
The values of Qxx10 and Ixx65 are read out during initialization and are
|
|
||||||
assumed to not change during operation.
|
|
||||||
*/
|
|
||||||
setAxisParamChecked(this, motorPositionDeadband,
|
|
||||||
counts_to_eu * deadband_counts / 16.0);
|
|
||||||
|
|
||||||
// Update the parameter library immediately
|
// Update the parameter library immediately
|
||||||
status = callParamCallbacks();
|
status = callParamCallbacks();
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
// If we can't communicate with the parameter library, it doesn't
|
// If we can't communicate with the parameter library, it doesn't make
|
||||||
// make sense to try and upstream this to the user -> Just log the
|
// sense to try and upstream this to the user -> Just log the error
|
||||||
// error
|
asynPrint(
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line "
|
"Controller \"%s\", axis %d => %s, line %d\ncallParamCallbacks "
|
||||||
"%d\ncallParamCallbacks "
|
"failed with %s.\n",
|
||||||
"failed with %s.\n",
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->stringifyAsynStatus(status));
|
||||||
pC_->stringifyAsynStatus(status));
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,13 +310,13 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
// If an error occurred while waiting for the handshake, abort
|
// If an error occurred while waiting for the handshake, abort the
|
||||||
// the waiting procedure and continue with the poll in order to
|
// waiting procedure and continue with the poll in order to handle
|
||||||
// handle the error.
|
// the error.
|
||||||
pTurboPmacA_->waitForHandshake = false;
|
pTurboPmacA_->waitForHandshake = false;
|
||||||
} else if (handshakePerformed == 1) {
|
} else if (handshakePerformed == 1) {
|
||||||
// Handshake has been performed successfully -> Continue with
|
// Handshake has been performed successfully -> Continue with the
|
||||||
// the poll
|
// poll
|
||||||
pTurboPmacA_->waitForHandshake = false;
|
pTurboPmacA_->waitForHandshake = false;
|
||||||
} else {
|
} else {
|
||||||
// Still waiting for the handshake - try again in the next busy
|
// Still waiting for the handshake - try again in the next busy
|
||||||
@@ -404,16 +356,15 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
The axis limits are set as: ({[]})
|
The axis limits are set as: ({[]})
|
||||||
where [] are the positive and negative limits set in EPICS/NICOS, {} are
|
where [] are the positive and negative limits set in EPICS/NICOS, {} are the
|
||||||
the software limits set on the MCU and () are the hardware limit
|
software limits set on the MCU and () are the hardware limit switches. In
|
||||||
switches. In other words, the EPICS/NICOS limits should be stricter than
|
other words, the EPICS/NICOS limits should be stricter than the software
|
||||||
the software limits on the MCU which in turn should be stricter than the
|
limits on the MCU which in turn should be stricter than the hardware limit
|
||||||
hardware limit switches. For example, if the hardware limit switches are
|
switches. For example, if the hardware limit switches are at [-10, 10], the
|
||||||
at [-10, 10], the software limits could be at [-9, 9] and the EPICS /
|
software limits could be at [-9, 9] and the EPICS / NICOS limits could be at
|
||||||
NICOS limits could be at
|
|
||||||
[-8, 8]. Therefore, we cannot use the software limits read from the MCU
|
[-8, 8]. Therefore, we cannot use the software limits read from the MCU
|
||||||
directly, but need to shrink them a bit. In this case, we're shrinking
|
directly, but need to shrink them a bit. In this case, we're shrinking them
|
||||||
them by limitsOffset on both sides.
|
by limitsOffset on both sides.
|
||||||
*/
|
*/
|
||||||
getAxisParamChecked(this, motorLimitsOffset, &limitsOffset);
|
getAxisParamChecked(this, motorLimitsOffset, &limitsOffset);
|
||||||
highLimit = highLimit - limitsOffset;
|
highLimit = highLimit - limitsOffset;
|
||||||
@@ -429,8 +380,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|||||||
(axStatus != -3 && axStatus != -5));
|
(axStatus != -3 && axStatus != -5));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the unique callsite identifier manually so it can be used
|
// Create the unique callsite identifier manually so it can be used later in
|
||||||
// later in the shouldBePrinted calls.
|
// the shouldBePrinted calls.
|
||||||
msgPrintControlKey keyStatus = msgPrintControlKey(
|
msgPrintControlKey keyStatus = msgPrintControlKey(
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
bool resetCountStatus = true;
|
bool resetCountStatus = true;
|
||||||
@@ -451,12 +402,12 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|||||||
|
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(keyStatus, true,
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyStatus, true,
|
||||||
pC_->pasynUser())) {
|
pC_->pasynUser())) {
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(
|
||||||
"Controller \"%s\", axis %d => %s, line "
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"%d\nEmergency stop "
|
"Controller \"%s\", axis %d => %s, line %d\nEmergency stop "
|
||||||
"activated.%s\n",
|
"activated.%s\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
pC_->getMsgPrintControl().getSuffix());
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
}
|
}
|
||||||
resetCountStatus = false;
|
resetCountStatus = false;
|
||||||
setAxisParamChecked(this, motorMessageText, "Emergency stop");
|
setAxisParamChecked(this, motorMessageText, "Emergency stop");
|
||||||
@@ -526,8 +477,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|||||||
*moving = false;
|
*moving = false;
|
||||||
break;
|
break;
|
||||||
case 15:
|
case 15:
|
||||||
// Move is interrupted, but the motor is not ready to receive
|
// Move is interrupted, but the motor is not ready to receive another
|
||||||
// another move command. Therefore it is treated as still moving.
|
// move command. Therefore it is treated as still moving.
|
||||||
*moving = true;
|
*moving = true;
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
@@ -570,6 +521,11 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|||||||
|
|
||||||
errorStatus = handleError(error, userMessage, sizeof(userMessage));
|
errorStatus = handleError(error, userMessage, sizeof(userMessage));
|
||||||
|
|
||||||
|
// Update the parameter library
|
||||||
|
if (error != 0) {
|
||||||
|
setAxisParamChecked(this, motorStatusProblem, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (*moving == false) {
|
if (*moving == false) {
|
||||||
setAxisParamChecked(this, motorMoveToHome, false);
|
setAxisParamChecked(this, motorMoveToHome, false);
|
||||||
}
|
}
|
||||||
@@ -597,6 +553,11 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|||||||
int sizeUserMessage) {
|
int sizeUserMessage) {
|
||||||
asynStatus status = asynError;
|
asynStatus status = asynError;
|
||||||
|
|
||||||
|
// Create the unique callsite identifier manually so it can be used later in
|
||||||
|
// the shouldBePrinted calls.
|
||||||
|
msgPrintControlKey keyError = msgPrintControlKey(
|
||||||
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
|
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case 0:
|
case 0:
|
||||||
status = asynSuccess;
|
status = asynSuccess;
|
||||||
@@ -605,9 +566,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|||||||
case 1:
|
case 1:
|
||||||
// EPICS should already prevent this issue in the first place,
|
// EPICS should already prevent this issue in the first place,
|
||||||
// since it contains the user limits
|
// since it contains the user limits
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nTarget "
|
"Controller \"%s\", axis %d => %s, line %d\nTarget "
|
||||||
"position would exceed user limits.%s\n",
|
"position would exceed user limits.%s\n",
|
||||||
@@ -619,70 +579,62 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
// Command not possible
|
// Command not possible
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
asynPrint(
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
|
||||||
"still moving, but received another move command. EPICS "
|
"still moving, but received another move command. EPICS "
|
||||||
"should prevent this, check if *moving is set "
|
"should prevent this, check if *moving is set correctly.%s\n",
|
||||||
"correctly.%s\n",
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
pC_->getMsgPrintControl().getSuffix());
|
|
||||||
}
|
}
|
||||||
setAxisParamChecked(this, motorMessageText,
|
setAxisParamChecked(this, motorMessageText,
|
||||||
"Axis received move command while it is still "
|
"Axis received move command while it is still "
|
||||||
"moving. Please call the support.");
|
"moving. Please call the support.");
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
asynPrint(
|
"Controller \"%s\", axis %d => %s, line %d\nAir cushion "
|
||||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
"feedback stopped during movement (P%2.2d01 = %d).%s\n",
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nLost feedback "
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
"from auxiliary device during movement (P%2.2d01 = "
|
axisNo_, error, pC_->getMsgPrintControl().getSuffix());
|
||||||
"%d).%s\n",
|
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
|
|
||||||
error, pC_->getMsgPrintControl().getSuffix());
|
|
||||||
}
|
}
|
||||||
snprintf(userMessage, sizeUserMessage,
|
snprintf(userMessage, sizeUserMessage,
|
||||||
"Lost feedback from auxiliary device during movement "
|
"Air cushion feedback stopped during movement (P%2.2d01 = "
|
||||||
"(P%2.2d01 = "
|
|
||||||
"%d). Please call the support.",
|
"%d). Please call the support.",
|
||||||
axisNo_, error);
|
axisNo_, error);
|
||||||
setAxisParamChecked(this, motorMessageText, userMessage);
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
asynPrint(
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nNo feedback "
|
"Controller \"%s\", axis %d => %s, line %d\nNo air cushion "
|
||||||
"from "
|
"feedback before movement start (P%2.2d01 = %d).%s\n",
|
||||||
"auxiliary device before movement (P%2.2d01 = %d).%s\n",
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
error, pC_->getMsgPrintControl().getSuffix());
|
||||||
axisNo_, error, pC_->getMsgPrintControl().getSuffix());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(
|
snprintf(userMessage, sizeUserMessage,
|
||||||
userMessage, sizeUserMessage,
|
"No air cushion feedback before movement start (P%2.2d01 = "
|
||||||
"No feedback from auxiliary device before movement (P%2.2d01 = "
|
"%d). Please call the support.",
|
||||||
"%d). Please call the support.",
|
axisNo_, error);
|
||||||
axisNo_, error);
|
|
||||||
setAxisParamChecked(this, motorMessageText, userMessage);
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 10:
|
||||||
/*
|
/*
|
||||||
Software limits of the controller have been hit. Since the EPICS
|
Software limits of the controller have been hit. Since the EPICS limits
|
||||||
limits are derived from the software limits and are a little bit
|
are derived from the software limits and are a little bit smaller, this
|
||||||
smaller, this error case can only happen if either the axis has an
|
error case can only happen if either the axis has an incremental encoder
|
||||||
incremental encoder which is not properly homed or if a bug occured.
|
which is not properly homed or if a bug occured.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
|
||||||
asynPrint(
|
asynPrint(
|
||||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nAxis hit the "
|
"Controller \"%s\", axis %d => %s, line %d\nAxis hit the "
|
||||||
@@ -701,15 +653,14 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|||||||
case 11:
|
case 11:
|
||||||
// Following error
|
// Following error
|
||||||
|
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
asynPrint(
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nMaximum "
|
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed "
|
||||||
"allowed "
|
"following error exceeded.%s\n",
|
||||||
"following error exceeded.%s\n",
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
pC_->getMsgPrintControl().getSuffix());
|
|
||||||
}
|
}
|
||||||
snprintf(userMessage, sizeUserMessage,
|
snprintf(userMessage, sizeUserMessage,
|
||||||
"Maximum allowed following error exceeded (P%2.2d01 = %d). "
|
"Maximum allowed following error exceeded (P%2.2d01 = %d). "
|
||||||
@@ -720,9 +671,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nSecurity "
|
"Controller \"%s\", axis %d => %s, line %d\nSecurity "
|
||||||
"input is triggered (P%2.2d01 = %d).%s\n",
|
"input is triggered (P%2.2d01 = %d).%s\n",
|
||||||
@@ -740,27 +690,25 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|||||||
case 13:
|
case 13:
|
||||||
// Driver hardware error triggered
|
// Driver hardware error triggered
|
||||||
|
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nDriver "
|
"Controller \"%s\", axis %d => %s, line %d\nDriver "
|
||||||
"hardware error triggered.%s\n",
|
"hardware error triggered.%s\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
pC_->getMsgPrintControl().getSuffix());
|
pC_->getMsgPrintControl().getSuffix());
|
||||||
}
|
}
|
||||||
snprintf(userMessage, sizeUserMessage,
|
snprintf(
|
||||||
"Driver hardware error (P%2.2d01 = 13). Please call the "
|
userMessage, sizeUserMessage,
|
||||||
"support.",
|
"Driver hardware error (P%2.2d01 = 13). Please call the support.",
|
||||||
axisNo_);
|
axisNo_);
|
||||||
setAxisParamChecked(this, motorMessageText, userMessage);
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
||||||
break;
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
// EPICS should already prevent this issue in the first place,
|
// EPICS should already prevent this issue in the first place,
|
||||||
// since it contains the user limits
|
// since it contains the user limits
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nMove "
|
"Controller \"%s\", axis %d => %s, line %d\nMove "
|
||||||
"command exceeds hardware limits (P%2.2d01 = %d).%s\n",
|
"command exceeds hardware limits (P%2.2d01 = %d).%s\n",
|
||||||
@@ -775,9 +723,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
pC_->pasynUser())) {
|
||||||
pC_->pasynUser())) {
|
|
||||||
asynPrint(
|
asynPrint(
|
||||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nUnknown error "
|
"Controller \"%s\", axis %d => %s, line %d\nUnknown error "
|
||||||
@@ -788,6 +735,10 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|||||||
setAxisParamChecked(this, motorMessageText, userMessage);
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status == asynSuccess) {
|
||||||
|
pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser());
|
||||||
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -800,7 +751,7 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
|||||||
|
|
||||||
char command[pC_->MAXBUF_] = {0};
|
char command[pC_->MAXBUF_] = {0};
|
||||||
char response[pC_->MAXBUF_] = {0};
|
char response[pC_->MAXBUF_] = {0};
|
||||||
double motorTargetPosition = 0.0;
|
double motorCoordinatesPosition = 0.0;
|
||||||
double motorRecResolution = 0.0;
|
double motorRecResolution = 0.0;
|
||||||
double motorVelocity = 0.0;
|
double motorVelocity = 0.0;
|
||||||
int enabled = 0;
|
int enabled = 0;
|
||||||
@@ -809,37 +760,32 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
|||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
// Suppress unused variables warning
|
|
||||||
(void)minVelocity;
|
|
||||||
(void)acceleration;
|
|
||||||
|
|
||||||
getAxisParamChecked(this, motorEnableRBV, &enabled);
|
getAxisParamChecked(this, motorEnableRBV, &enabled);
|
||||||
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
||||||
|
|
||||||
if (enabled == 0) {
|
if (enabled == 0) {
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"disabled.\n",
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is disabled.\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
return asynSuccess;
|
return asynSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert from EPICS to user / motor units
|
// Convert from EPICS to user / motor units
|
||||||
motorTargetPosition = position * motorRecResolution;
|
motorCoordinatesPosition = position * motorRecResolution;
|
||||||
motorVelocity = maxVelocity * motorRecResolution;
|
motorVelocity = maxVelocity * motorRecResolution;
|
||||||
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nStart of axis to "
|
"Controller \"%s\", axis %d => %s, line %d\nStart of axis to "
|
||||||
"position %lf.\n",
|
"position %lf.\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
|
||||||
motorTargetPosition);
|
|
||||||
|
|
||||||
// Check if the speed is allowed to be changed
|
// Check if the speed is allowed to be changed
|
||||||
getAxisParamChecked(this, motorCanSetSpeed, &motorCanSetSpeed);
|
getAxisParamChecked(this, motorCanSetSpeed, &motorCanSetSpeed);
|
||||||
|
|
||||||
// Prepend the new motor speed, if the user is allowed to set the speed.
|
// Prepend the new motor speed, if the user is allowed to set the speed.
|
||||||
// Mind the " " (space) before the closing "", as the command created
|
// Mind the " " (space) before the closing "", as the command created here
|
||||||
// here is prepended to the one down below.
|
// is prepended to the one down below.
|
||||||
if (motorCanSetSpeed != 0) {
|
if (motorCanSetSpeed != 0) {
|
||||||
snprintf(command, sizeof(command), "Q%2.2d04=%lf ", axisNo_,
|
snprintf(command, sizeof(command), "Q%2.2d04=%lf ", axisNo_,
|
||||||
motorVelocity);
|
motorVelocity);
|
||||||
@@ -852,34 +798,34 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
|||||||
motorVelocity);
|
motorVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform handshake, Set target position (and speed, if allowed) and
|
// Perform handshake, Set target position (and speed, if allowed) and start
|
||||||
// start the move command
|
// the move command
|
||||||
if (relative) {
|
if (relative) {
|
||||||
snprintf(&command[writeOffset], sizeof(command) - writeOffset,
|
snprintf(&command[writeOffset], sizeof(command) - writeOffset,
|
||||||
"P%2.2d23=0 Q%2.2d02=%lf M%2.2d=2", axisNo_, axisNo_,
|
"P%2.2d23=0 Q%2.2d02=%lf M%2.2d=2", axisNo_, axisNo_,
|
||||||
motorTargetPosition, axisNo_);
|
motorCoordinatesPosition, axisNo_);
|
||||||
} else {
|
} else {
|
||||||
snprintf(&command[writeOffset], sizeof(command) - writeOffset,
|
snprintf(&command[writeOffset], sizeof(command) - writeOffset,
|
||||||
"P%2.2d23=0 Q%2.2d01=%lf M%2.2d=1", axisNo_, axisNo_,
|
"P%2.2d23=0 Q%2.2d01=%lf M%2.2d=1", axisNo_, axisNo_,
|
||||||
motorTargetPosition, axisNo_);
|
motorCoordinatesPosition, axisNo_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't expect an answer
|
// We don't expect an answer
|
||||||
status = pC_->writeRead(axisNo_, command, response, 0);
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nStarting "
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"movement to "
|
"Controller \"%s\", axis %d => %s, line %d\nStarting movement to "
|
||||||
"target position %lf failed.\n",
|
"target position %lf failed.\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
motorTargetPosition);
|
motorCoordinatesPosition);
|
||||||
setAxisParamChecked(this, motorStatusProblem, true);
|
setAxisParamChecked(this, motorStatusProblem, true);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the next poll, we will check if the handshake has been performed
|
// In the next poll, we will check if the handshake has been performed in a
|
||||||
// in a reasonable time
|
// reasonable time
|
||||||
pTurboPmacA_->waitForHandshake = true;
|
pTurboPmacA_->waitForHandshake = true;
|
||||||
pTurboPmacA_->timeAtHandshake = time(NULL);
|
pTurboPmacA_->timeAtHandshake = time(NULL);
|
||||||
|
|
||||||
@@ -901,32 +847,29 @@ asynStatus turboPmacAxis::stop(double acceleration) {
|
|||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
// Suppress unused variables warning
|
|
||||||
(void)acceleration;
|
|
||||||
|
|
||||||
snprintf(command, sizeof(command), "M%2.2d=8", axisNo_);
|
snprintf(command, sizeof(command), "M%2.2d=8", axisNo_);
|
||||||
status = pC_->writeRead(axisNo_, command, response, 0);
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
||||||
|
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nStopping "
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"the movement "
|
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
|
||||||
"failed.\n",
|
"failed.\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
setAxisParamChecked(this, motorStatusProblem, true);
|
setAxisParamChecked(this, motorStatusProblem, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the driver to idle state and move out of the handshake wait
|
// Reset the driver to idle state and move out of the handshake wait loop,
|
||||||
// loop, if we're currently inside it.
|
// if we're currently inside it.
|
||||||
pTurboPmacA_->waitForHandshake = false;
|
pTurboPmacA_->waitForHandshake = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Stopping the motor results in a movement and further move commands have
|
Stopping the motor results in a movement and further move commands have to
|
||||||
to wait until the stopping movement is done. Therefore, we need to wait
|
wait until the stopping movement is done. Therefore, we need to wait until
|
||||||
until the poller "sees" the changed state (otherwise, we risk issuing
|
the poller "sees" the changed state (otherwise, we risk issuing move
|
||||||
move commands while the motor is stopping). To ensure that at least one
|
commands while the motor is stopping). To ensure that at least one poll is
|
||||||
poll is done, this thread (which also runs move commands) is paused for
|
done, this thread (which also runs move commands) is paused for twice the
|
||||||
twice the idle poll period.
|
idle poll period.
|
||||||
*/
|
*/
|
||||||
unsigned int idlePollMicros =
|
unsigned int idlePollMicros =
|
||||||
(unsigned int)ceil(pC_->idlePollPeriod() * 1e6);
|
(unsigned int)ceil(pC_->idlePollPeriod() * 1e6);
|
||||||
@@ -957,8 +900,8 @@ asynStatus turboPmacAxis::doReset() {
|
|||||||
setAxisParamChecked(this, motorStatusProblem, true);
|
setAxisParamChecked(this, motorStatusProblem, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the driver to idle state and move out of the handshake wait
|
// Reset the driver to idle state and move out of the handshake wait loop,
|
||||||
// loop, if we're currently inside it.
|
// if we're currently inside it.
|
||||||
pTurboPmacA_->waitForHandshake = false;
|
pTurboPmacA_->waitForHandshake = false;
|
||||||
|
|
||||||
// Disable the axis
|
// Disable the axis
|
||||||
@@ -968,7 +911,7 @@ asynStatus turboPmacAxis::doReset() {
|
|||||||
/*
|
/*
|
||||||
Home the axis. On absolute encoder systems, this is a no-op
|
Home the axis. On absolute encoder systems, this is a no-op
|
||||||
*/
|
*/
|
||||||
asynStatus turboPmacAxis::doHome(double minVelocity, double maxVelocity,
|
asynStatus turboPmacAxis::doHome(double min_velocity, double max_velocity,
|
||||||
double acceleration, int forwards) {
|
double acceleration, int forwards) {
|
||||||
|
|
||||||
// Status of read-write-operations of ASCII commands to the controller
|
// Status of read-write-operations of ASCII commands to the controller
|
||||||
@@ -979,12 +922,6 @@ asynStatus turboPmacAxis::doHome(double minVelocity, double maxVelocity,
|
|||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
// Suppress unused variables warning
|
|
||||||
(void)minVelocity;
|
|
||||||
(void)maxVelocity;
|
|
||||||
(void)acceleration;
|
|
||||||
(void)forwards;
|
|
||||||
|
|
||||||
getAxisParamChecked(this, encoderType, &response);
|
getAxisParamChecked(this, encoderType, &response);
|
||||||
|
|
||||||
// Only send the home command if the axis has an incremental encoder
|
// Only send the home command if the axis has an incremental encoder
|
||||||
@@ -1056,14 +993,13 @@ asynStatus turboPmacAxis::readEncoderType() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is not a command that can always be run when enabling a motor as it
|
This is not a command that can always be run when enabling a motor as it also
|
||||||
also causes relative encoders to reread a position necessitating
|
causes relative encoders to reread a position necessitating recalibration. We
|
||||||
recalibration. We only want it to run on absolute encoders. We also want it
|
only want it to run on absolute encoders. We also want it to be clear to
|
||||||
to be clear to instrument scientists, that power has to be cut to the motor,
|
instrument scientists, that power has to be cut to the motor, in order to reread
|
||||||
in order to reread the encoder as not all motors have breaks and they may
|
the encoder as not all motors have breaks and they may start to move when
|
||||||
start to move when disabled. For that reason, we don't automatically disable
|
disabled. For that reason, we don't automatically disable the motors to run the
|
||||||
the motors to run the command and instead require that the scientists first
|
command and instead require that the scientists first disable the motor.
|
||||||
disable the motor.
|
|
||||||
*/
|
*/
|
||||||
asynStatus turboPmacAxis::rereadEncoder() {
|
asynStatus turboPmacAxis::rereadEncoder() {
|
||||||
char command[pC_->MAXBUF_] = {0};
|
char command[pC_->MAXBUF_] = {0};
|
||||||
@@ -1107,12 +1043,11 @@ asynStatus turboPmacAxis::rereadEncoder() {
|
|||||||
return asynError;
|
return asynError;
|
||||||
} else {
|
} else {
|
||||||
snprintf(command, sizeof(command), "M%2.2d=15", axisNo_);
|
snprintf(command, sizeof(command), "M%2.2d=15", axisNo_);
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
asynPrint(
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nRereading "
|
pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||||
"absolute "
|
"Controller \"%s\", axis %d => %s, line %d\nRereading absolute "
|
||||||
"encoder via command %s.\n",
|
"encoder via command %s.\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, command);
|
||||||
command);
|
|
||||||
pC_->writeRead(axisNo_, command, response, 0);
|
pC_->writeRead(axisNo_, command, response, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1121,8 +1056,8 @@ asynStatus turboPmacAxis::rereadEncoder() {
|
|||||||
// it is actually finished, so we instead wait for 0.5 seconds.
|
// it is actually finished, so we instead wait for 0.5 seconds.
|
||||||
usleep(500000);
|
usleep(500000);
|
||||||
|
|
||||||
// Turn off parameter as finished rereading, this will only be
|
// Turn off parameter as finished rereading, this will only be immediately
|
||||||
// immediately noticed in the read back variable though
|
// noticed in the read back variable though
|
||||||
setAxisParamChecked(this, rereadEncoderPosition, false);
|
setAxisParamChecked(this, rereadEncoderPosition, false);
|
||||||
return asynSuccess;
|
return asynSuccess;
|
||||||
}
|
}
|
||||||
@@ -1142,28 +1077,27 @@ asynStatus turboPmacAxis::enable(bool on) {
|
|||||||
/*
|
/*
|
||||||
Continue regardless of the status returned by the poll; we just want to
|
Continue regardless of the status returned by the poll; we just want to
|
||||||
find out whether the motor is currently moving or not. If the poll
|
find out whether the motor is currently moving or not. If the poll
|
||||||
function fails before it can determine that, it is assumed that the
|
function fails before it can determine that, it is assumed that the motor
|
||||||
motor is not moving.
|
is not moving.
|
||||||
*/
|
*/
|
||||||
bool moving = false;
|
bool moving = false;
|
||||||
doPoll(&moving);
|
doPoll(&moving);
|
||||||
|
|
||||||
// If the axis is currently moving, it cannot be disabled. Ignore the
|
// If the axis is currently moving, it cannot be disabled. Ignore the
|
||||||
// command and inform the user. We check the last known status of the
|
// command and inform the user. We check the last known status of the axis
|
||||||
// axis instead of "moving", since status -6 is also moving, but the
|
// instead of "moving", since status -6 is also moving, but the motor can
|
||||||
// motor can actually be disabled in this state!
|
// actually be disabled in this state!
|
||||||
int axStatus = pTurboPmacA_->axisStatus;
|
int axStatus = pTurboPmacA_->axisStatus;
|
||||||
if (axStatus == 1 || axStatus == 2 || axStatus == 3 || axStatus == 4 ||
|
if (axStatus == 1 || axStatus == 2 || axStatus == 3 || axStatus == 4 ||
|
||||||
axStatus == 5 || axStatus == 6 || axStatus == 7 || axStatus == 8 ||
|
axStatus == 5 || axStatus == 6 || axStatus == 7 || axStatus == 8 ||
|
||||||
axStatus == 9 || axStatus == 10 || axStatus == 11 || axStatus == 12 ||
|
axStatus == 9 || axStatus == 10 || axStatus == 11 || axStatus == 12 ||
|
||||||
axStatus == 13 || axStatus == 15 || axStatus == 16) {
|
axStatus == 13 || axStatus == 15 || axStatus == 16) {
|
||||||
|
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"idle (status %d) and can therefore not be enabled / "
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
||||||
"disabled.\n",
|
"idle (status %d) and can therefore not be enabled / disabled.\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axStatus);
|
||||||
axStatus);
|
|
||||||
|
|
||||||
setAxisParamChecked(this, motorMessageText,
|
setAxisParamChecked(this, motorMessageText,
|
||||||
"Axis cannot be disabled while it is moving.");
|
"Axis cannot be disabled while it is moving.");
|
||||||
@@ -1174,11 +1108,11 @@ asynStatus turboPmacAxis::enable(bool on) {
|
|||||||
// Axis is already enabled / disabled and a new enable / disable command
|
// Axis is already enabled / disabled and a new enable / disable command
|
||||||
// was sent => Do nothing
|
// was sent => Do nothing
|
||||||
if ((axStatus != -3) == on) {
|
if ((axStatus != -3) == on) {
|
||||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_WARNING,
|
asynPrint(
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
|
pC_->pasynUser(), ASYN_TRACE_WARNING,
|
||||||
"already %s.\n",
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is already %s.\n",
|
||||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||||
on ? "enabled" : "disabled");
|
on ? "enabled" : "disabled");
|
||||||
return asynSuccess;
|
return asynSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1327,8 +1261,8 @@ asynStatus turboPmacCreateAxis(const char *portName, int axis) {
|
|||||||
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
|
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
|
||||||
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp
|
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp
|
||||||
|
|
||||||
The created object is registered in EPICS in its constructor and can
|
The created object is registered in EPICS in its constructor and can safely
|
||||||
safely be "leaked" here.
|
be "leaked" here.
|
||||||
*/
|
*/
|
||||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||||
@@ -1351,15 +1285,14 @@ static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
|
|||||||
static const iocshFuncDef configTurboPmacCreateAxis = {
|
static const iocshFuncDef configTurboPmacCreateAxis = {
|
||||||
"turboPmacAxis", 2, CreateAxisArgs,
|
"turboPmacAxis", 2, CreateAxisArgs,
|
||||||
"Create an instance of a turboPmac axis. The first argument is the "
|
"Create an instance of a turboPmac axis. The first argument is the "
|
||||||
"controller this axis should be attached to, the second argument is "
|
"controller this axis should be attached to, the second argument is the "
|
||||||
"the "
|
|
||||||
"axis number."};
|
"axis number."};
|
||||||
static void configTurboPmacCreateAxisCallFunc(const iocshArgBuf *args) {
|
static void configTurboPmacCreateAxisCallFunc(const iocshArgBuf *args) {
|
||||||
turboPmacCreateAxis(args[0].sval, args[1].ival);
|
turboPmacCreateAxis(args[0].sval, args[1].ival);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is made known to EPICS in turboPmac.dbd and is called by
|
// This function is made known to EPICS in turboPmac.dbd and is called by EPICS
|
||||||
// EPICS in order to register both functions in the IOC shell
|
// in order to register both functions in the IOC shell
|
||||||
static void turboPmacAxisRegister(void) {
|
static void turboPmacAxisRegister(void) {
|
||||||
iocshRegister(&configTurboPmacCreateAxis,
|
iocshRegister(&configTurboPmacCreateAxis,
|
||||||
configTurboPmacCreateAxisCallFunc);
|
configTurboPmacCreateAxisCallFunc);
|
||||||
|
|||||||
@@ -4,19 +4,15 @@
|
|||||||
#include "turboPmacController.h"
|
#include "turboPmacController.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
struct HIDDEN turboPmacAxisImpl;
|
struct turboPmacAxisImpl;
|
||||||
|
|
||||||
class HIDDEN turboPmacAxis : public sinqAxis {
|
class turboPmacAxis : public sinqAxis {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new turboPmacAxis
|
* @brief Construct a new turboPmacAxis
|
||||||
*
|
*
|
||||||
* @param pController Pointer to the associated controller
|
* @param pController Pointer to the associated controller
|
||||||
* @param axisNo Index of the axis
|
* @param axisNo Index of the axis
|
||||||
* @param initialize By setting this parameter to false, the
|
|
||||||
* initialization functions of the axes are not executed. This is e.g.
|
|
||||||
* necessary when this constructor is called from a children class
|
|
||||||
* constructor which performs its own initialization.
|
|
||||||
*/
|
*/
|
||||||
turboPmacAxis(turboPmacController *pController, int axisNo,
|
turboPmacAxis(turboPmacController *pController, int axisNo,
|
||||||
bool initialize = true);
|
bool initialize = true);
|
||||||
@@ -28,18 +24,6 @@ class HIDDEN turboPmacAxis : public sinqAxis {
|
|||||||
*/
|
*/
|
||||||
virtual ~turboPmacAxis();
|
virtual ~turboPmacAxis();
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Readout of some values from the controller at IOC startup
|
|
||||||
*
|
|
||||||
* The following steps are performed:
|
|
||||||
* - Read out the motor status, motor position, velocity and acceleration
|
|
||||||
* from the MCU and store this information in the parameter library.
|
|
||||||
* - Set the enable PV according to the initial status of the axis.
|
|
||||||
*
|
|
||||||
* @return asynStatus
|
|
||||||
*/
|
|
||||||
virtual asynStatus init();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Implementation of the `stop` function from asynMotorAxis
|
* @brief Implementation of the `stop` function from asynMotorAxis
|
||||||
*
|
*
|
||||||
@@ -86,6 +70,18 @@ class HIDDEN turboPmacAxis : public sinqAxis {
|
|||||||
double min_velocity, double max_velocity,
|
double min_velocity, double max_velocity,
|
||||||
double acceleration);
|
double acceleration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout of some values from the controller at IOC startup
|
||||||
|
*
|
||||||
|
* The following steps are performed:
|
||||||
|
* - Read out the motor status, motor position, velocity and acceleration
|
||||||
|
* from the MCU and store this information in the parameter library.
|
||||||
|
* - Set the enable PV according to the initial status of the axis.
|
||||||
|
*
|
||||||
|
* @return asynStatus
|
||||||
|
*/
|
||||||
|
virtual asynStatus init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Implementation of the `doReset` function from sinqAxis.
|
* @brief Implementation of the `doReset` function from sinqAxis.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ struct turboPmacControllerImpl {
|
|||||||
// User for writing int32 values to the port driver.
|
// User for writing int32 values to the port driver.
|
||||||
asynUser *pasynInt32SyncIOipPort;
|
asynUser *pasynInt32SyncIOipPort;
|
||||||
|
|
||||||
// Indices of additional ParamLib entries
|
// Indices of additional PVs
|
||||||
int rereadEncoderPosition;
|
int rereadEncoderPosition;
|
||||||
int readConfig;
|
int readConfig;
|
||||||
int flushHardware;
|
int flushHardware;
|
||||||
int limFromHardware;
|
int limFromHardware;
|
||||||
};
|
};
|
||||||
#define NUM_turboPmac_DRIVER_PARAMS 5
|
#define NUM_turboPmac_DRIVER_PARAMS 3
|
||||||
|
|
||||||
turboPmacController::turboPmacController(const char *portName,
|
turboPmacController::turboPmacController(const char *portName,
|
||||||
const char *ipPortConfigName,
|
const char *ipPortConfigName,
|
||||||
@@ -61,17 +61,17 @@ turboPmacController::turboPmacController(const char *portName,
|
|||||||
- REREAD_ENCODER_POSITION
|
- REREAD_ENCODER_POSITION
|
||||||
- READ_CONFIG
|
- READ_CONFIG
|
||||||
*/
|
*/
|
||||||
numExtraParams + NUM_turboPmac_DRIVER_PARAMS),
|
numExtraParams + NUM_turboPmac_DRIVER_PARAMS)
|
||||||
pTurboPmacC_(
|
|
||||||
std::make_unique<turboPmacControllerImpl>((turboPmacControllerImpl){
|
{
|
||||||
.comTimeout = comTimeout,
|
|
||||||
.lastResponse = {0},
|
// The paramLib indices are populated with the calls to createParam
|
||||||
.pasynInt32SyncIOipPort = nullptr, // Populated in constructor
|
pTurboPmacC_ =
|
||||||
.rereadEncoderPosition = 0, // Populated in constructor
|
std::make_unique<turboPmacControllerImpl>((turboPmacControllerImpl){
|
||||||
.readConfig = 0, // Populated in constructor
|
.comTimeout = comTimeout,
|
||||||
.flushHardware = 0, // Populated in constructor
|
.lastResponse = {0},
|
||||||
.limFromHardware = 0, // Populated in constructor
|
});
|
||||||
})) {
|
|
||||||
// Initialization of local variables
|
// Initialization of local variables
|
||||||
asynStatus status = asynSuccess;
|
asynStatus status = asynSuccess;
|
||||||
|
|
||||||
@@ -206,6 +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 timeoutStatus = asynSuccess;
|
||||||
char drvMessageText[MAXBUF_] = {0};
|
char drvMessageText[MAXBUF_] = {0};
|
||||||
char modResponse[MAXBUF_] = {0};
|
char modResponse[MAXBUF_] = {0};
|
||||||
int motorStatusProblem = 0;
|
int motorStatusProblem = 0;
|
||||||
@@ -253,21 +254,9 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_,
|
pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_,
|
||||||
pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn, &eomReason);
|
pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn, &eomReason);
|
||||||
|
|
||||||
/*
|
// if (strchr(command, '=')) {
|
||||||
If sth. is written to the controller, it needs some time to process the
|
// usleep(20000);
|
||||||
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__);
|
||||||
@@ -283,7 +272,8 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
getMsgPrintControl().getSuffix());
|
getMsgPrintControl().getSuffix());
|
||||||
}
|
}
|
||||||
|
|
||||||
checkComTimeoutWatchdog(axisNo, drvMessageText, sizeof(drvMessageText));
|
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
|
||||||
|
sizeof(drvMessageText));
|
||||||
|
|
||||||
int timeoutCounter = 0;
|
int timeoutCounter = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -331,10 +321,14 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
axis->setNeedInit(true);
|
axis->setNeedInit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (timeoutStatus == asynError) {
|
||||||
|
status = asynError;
|
||||||
|
}
|
||||||
|
|
||||||
// The message should only ever terminate due to reason 2
|
// The message should only ever terminate due to reason 2
|
||||||
msgPrintControlKey terminateKey =
|
msgPrintControlKey terminateKey =
|
||||||
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||||
if (status == asynSuccess && eomReason != 2) {
|
if (eomReason != 2) {
|
||||||
status = asynError;
|
status = asynError;
|
||||||
|
|
||||||
char reasonStringified[30] = {0};
|
char reasonStringified[30] = {0};
|
||||||
@@ -385,7 +379,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
}
|
}
|
||||||
msgPrintControlKey numResponsesKey =
|
msgPrintControlKey numResponsesKey =
|
||||||
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||||
if (status == asynSuccess && numExpectedResponses != numReceivedResponses) {
|
if (numExpectedResponses != numReceivedResponses) {
|
||||||
adjustResponseForPrint(modResponse, response, MAXBUF_);
|
adjustResponseForPrint(modResponse, response, MAXBUF_);
|
||||||
|
|
||||||
if (getMsgPrintControl().shouldBePrinted(numResponsesKey, true,
|
if (getMsgPrintControl().shouldBePrinted(numResponsesKey, true,
|
||||||
@@ -567,8 +561,7 @@ 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);
|
||||||
|
|||||||
@@ -15,11 +15,11 @@
|
|||||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||||
// between the controller and the axis .h-file. See
|
// between the controller and the axis .h-file. See
|
||||||
// https://en.cppreference.com/w/cpp/language/class.
|
// https://en.cppreference.com/w/cpp/language/class.
|
||||||
class HIDDEN turboPmacAxis;
|
class turboPmacAxis;
|
||||||
|
|
||||||
struct HIDDEN turboPmacControllerImpl;
|
struct turboPmacControllerImpl;
|
||||||
|
|
||||||
class HIDDEN turboPmacController : public sinqController {
|
class turboPmacController : public sinqController {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new turboPmacController object. This function is meant
|
* @brief Construct a new turboPmacController object. This function is meant
|
||||||
@@ -93,8 +93,7 @@ class HIDDEN 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
|
||||||
|
|||||||
Reference in New Issue
Block a user