Compare commits

...

10 Commits
0.1 ... 0.3.0

Author SHA1 Message Date
3ee507086a Included the possibility to vary the motor speed. 2024-12-06 08:30:10 +01:00
2e2c24238b Prototype for v0.2 2024-12-04 13:39:36 +01:00
e967e65d33 Added support for (optional) variable speed drive mode and refactored
some records into sinqMotor
2024-11-29 14:54:05 +01:00
dc70b560f7 Improved the error message when the MCU response is printed and the IOC
shell constructor documentation.
2024-11-27 16:05:48 +01:00
a6227629ad Improved the error message when the MCU response is printed. 2024-11-27 15:51:03 +01:00
2dd46cc48d Improved the error message when the error is printed. 2024-11-27 15:36:51 +01:00
35ade9e78d Substantial rework of the driver after CAMEA testing showed various
flaws.
2024-11-26 16:59:18 +01:00
a69ffe8134 Changed sinqMotor dependency to 0.2.0 2024-11-21 15:38:38 +01:00
a325a0b8c7 Merge branch 'fixci' into 'main'
Fix CI Build

See merge request sinq-epics-modules/pmacv3!1
2024-11-20 14:54:34 +01:00
63dba55673 Fix CI Build 2024-11-20 14:54:34 +01:00
9 changed files with 383 additions and 438 deletions

View File

@ -37,14 +37,21 @@ formatting:
build_module: build_module:
stage: build stage: build
script: 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 - sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile - echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
- make install - make install
- cp -rT "/ioc/modules/pmacV3/$(ls -U /ioc/modules/pmacV3/ | head -1)" "./pmacV3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}" - cp -rT "/ioc/modules/pmacv3/$(ls -U /ioc/modules/pmacv3/ | head -1)" "./pmacv3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
artifacts: artifacts:
name: "pmacV3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}" name: "pmacv3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
paths: paths:
- "pmacV3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*" - "pmacv3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week expire_in: 1 week
when: always when: always
tags: tags:

View File

@ -1,7 +1,7 @@
# Use the PSI build system # Use the PSI build system
include /ioc/tools/driver.makefile include /ioc/tools/driver.makefile
MODULE=pmacV3 MODULE=pmacv3
BUILDCLASSES=Linux BUILDCLASSES=Linux
EPICS_VERSIONS=7.0.7 EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL% ARCH_FILTER=RHEL%
@ -11,17 +11,20 @@ REQUIRED+=asynMotor
REQUIRED+=sinqMotor REQUIRED+=sinqMotor
# Specify the version of sinqMotor we want to build against # Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=0.1 sinqMotor_VERSION=0.4.0
# These headers allow to depend on this library for derived drivers. # These headers allow to depend on this library for derived drivers.
HEADERS += src/pmacV3Axis.h HEADERS += src/pmacv3Axis.h
HEADERS += src/pmacV3Controller.h HEADERS += src/pmacv3Controller.h
# Source files to build # Source files to build
SOURCES += src/pmacV3Axis.cpp SOURCES += src/pmacv3Axis.cpp
SOURCES += src/pmacV3Controller.cpp SOURCES += src/pmacv3Controller.cpp
# Store the record files
TEMPLATES += db/pmacv3.db
# This file registers the motor-specific functions in the IOC shell. # This file registers the motor-specific functions in the IOC shell.
DBDS += src/pmacV3.dbd DBDS += src/pmacv3.dbd
USR_CFLAGS += -Wall -Wextra # -Werror USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result # -Werror

View File

@ -1,20 +1,42 @@
# pmacV3 # pmacv3
## Overview ## Overview
This is a driver for the PMac V3 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 pmacV3 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.
## Usage in IOC shell ## User guide
pmacV3 exposes the following IOC shell functions (all in pmacV3Controller.cpp): This driver is a standard sinqMotor-derived driver and does not need any specific configuration. For the general configuration, please see https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
- `pmacV3CreateController`: Create a new controller object.
- `pmacV3CreateAxis`: Create a new axis object.
The function arguments are documented directly within the source code.
## Versioning ## Developer guide
### Usage in IOC shell
pmacv3 exposes the following IOC shell functions (all in pmacv3Controller.cpp):
- `pmacv3Controller`: Create a new controller object.
- `pmacv3Axis`: Create a new axis object.
These functions are parametrized as follows:
```
pmacv3Controller(
"$(NAME)", # Name of the MCU, e.g. mcu1. This parameter should be provided by an environment variable.
"$(ASYN_PORT)", # IP-Port of the MCU. This parameter should be provided by an environment variable.
8, # Maximum number of axes
0.05, # Busy poll period in seconds
1, # Idle poll period in seconds
0.05 # Communication timeout in seconds
);
```
```
pmacv3Axis(
"$(NAME)", # Name of the associated MCU, e.g. mcu1. This parameter should be provided by an environment variable.
1 # Index of the axis.
);
```
### 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://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/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. Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.

30
db/pmacv3.db Normal file
View File

@ -0,0 +1,30 @@
# Read out the encoder type in human-readable form. The output numbers can be
# interpreted as ASCII.
# This record is coupled to the parameter library via encoderType -> ENCODER_TYPE.
record(waveform, "$(INSTR)$(M):Encoder_Type") {
field(DTYP, "asynOctetRead")
field(INP, "@asyn($(CONTROLLER),$(AXIS),1) ENCODER_TYPE")
field(FTVL, "CHAR")
field(NELM, "80")
field(SCAN, "I/O Intr")
}
# Trigger a rereading of the encoder. This action is sometimes necessary for
# absolute encoders after enabling them. For incremental encoders, this is a no-op.
# This record is coupled to the parameter library via rereadEncoderPosition_ -> REREAD_ENCODER_POSITION.
record(longout, "$(INSTR)$(M):Reread_Encoder") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) REREAD_ENCODER_POSITION")
field(PINI, "NO")
}
# The pmacV3 driver reads certain configuration parameters (such as the velocity
# and the acceleration) directly from the MCU. This reading procedure is performed
# once at IOC startup during atFirstPoll. However, it can be triggered manually
# by setting this record value to 1.
# This record is coupled to the parameter library via readConfig_ -> READ_CONFIG.
record(longout, "$(INSTR)$(M):Read_Config") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) READ_CONFIG")
field(PINI, "NO")
}

View File

@ -1,4 +1,4 @@
#--------------------------------------------- #---------------------------------------------
# SINQ specific DB definitions # SINQ specific DB definitions
#--------------------------------------------- #---------------------------------------------
registrar(pmacV3Register) registrar(pmacv3Register)

View File

@ -1,6 +1,6 @@
#include "pmacV3Axis.h" #include "pmacv3Axis.h"
#include "asynOctetSyncIO.h" #include "asynOctetSyncIO.h"
#include "pmacV3Controller.h" #include "pmacv3Controller.h"
#include <cmath> #include <cmath>
#include <errlog.h> #include <errlog.h>
#include <limits> #include <limits>
@ -8,7 +8,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
pmacV3Axis::pmacV3Axis(pmacV3Controller *pC, int axisNo, double offsetLimit) pmacv3Axis::pmacv3Axis(pmacv3Controller *pC, int axisNo)
: sinqAxis(pC, axisNo), pC_(pC) { : sinqAxis(pC, axisNo), pC_(pC) {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
@ -38,7 +38,6 @@ pmacV3Axis::pmacV3Axis(pmacV3Controller *pC, int axisNo, double offsetLimit)
initial_poll_ = true; initial_poll_ = true;
waitForHandshake_ = false; waitForHandshake_ = false;
axisStatus_ = 0; axisStatus_ = 0;
offsetLimits_ = offsetLimit;
// 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);
@ -66,7 +65,7 @@ pmacV3Axis::pmacV3Axis(pmacV3Controller *pC, int axisNo, double offsetLimit)
scaleMovTimeout_ = 2.0; scaleMovTimeout_ = 2.0;
} }
pmacV3Axis::~pmacV3Axis(void) { pmacv3Axis::~pmacv3Axis(void) {
// Since the controller memory is managed somewhere else, we don't need to // Since the controller memory is managed somewhere else, we don't need to
// clean up the pointer pC here. // clean up the pointer pC here.
} }
@ -74,12 +73,12 @@ pmacV3Axis::~pmacV3Axis(void) {
/** /**
Read the configuration at the first poll Read the configuration at the first poll
*/ */
asynStatus pmacV3Axis::atFirstPoll() { return readConfig(); } asynStatus pmacv3Axis::atFirstPoll() { return readConfig(); }
/* /*
Read the configuration from the motor control unit and the parameter library. Read the configuration from the motor control unit and the parameter library.
*/ */
asynStatus pmacV3Axis::readConfig() { asynStatus pmacv3Axis::readConfig() {
// Local variable declaration // Local variable declaration
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
@ -87,60 +86,13 @@ asynStatus pmacV3Axis::readConfig() {
int nvals = 0; int nvals = 0;
double motorRecResolution = 0.0; double motorRecResolution = 0.0;
double motorPosition = 0.0; double motorPosition = 0.0;
double motorVelBase = 0.0; double motorVelocity = 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
// the air cushions in milliseconds.
int axStatus = 0; int axStatus = 0;
/*
The motor record resolution (index motorRecResolution_ in the parameter
library, MRES in the motor record) is NOT a conversion factor between user
units (e.g. mm) and motor units (e.g. encoder steps), but a scaling factor
defining the resolution of the position readback field RRBV. This is due to
an implementation detail inside EPICS described here:
https://epics.anl.gov/tech-talk/2018/msg00089.php
https://github.com/epics-modules/motor/issues/8
Basically, the position value in the parameter library is a double which is
then truncated to an integer in devMotorAsyn.c. Therefore, if we want a
precision of 1 millimeter, we need to set MRES to 1. If we want one of 1
micrometer, we need to set MRES to 0.001. The readback value needs to be
multiplied with MRES to get the actual value.
In the driver, we use user units. Therefore, when we interact with the
parameter library, we need to account for MRES. This means:
- When writing position or speed to the parameter library, we divide the
value by the motor record resolution.
- When reading position or speed from the parameter library, we multiply the
value with the motor record resolution.
Index and motor record field are coupled as follows:
The parameter motorRecResolution_ is coupled to the field MRES of the motor
record in the following manner:
- In sinqMotor.db, the PV (motor_record_pv_name) MOTOR_REC_RESOLUTION
is defined as a copy of the field (motor_record_pv_name).MRES:
record(ao,"$(P)$(M):Resolution") {
field(DESC, "$(M) resolution")
field(DOL, "$(P)$(M).MRES CP MS")
field(OMSL, "closed_loop")
field(DTYP, "asynFloat64")
field(OUT, "@asyn($(PORT),$(ADDR))MOTOR_REC_RESOLUTION")
field(PREC, "$(PREC)")
}
- The PV name MOTOR_REC_RESOLUTION is coupled in asynMotorController.h to
the constant motorRecResolutionString
- ... which in turn is assigned to motorRecResolution_ in
asynMotorController.cpp This way of making the field visible to the driver
is described here: https://epics.anl.gov/tech-talk/2020/msg00378.php This is
a one-way coupling, changes to the parameter library via setDoubleParam are
NOT transferred to (motor_record_pv_name).MRES or to
(motor_record_pv_name):Resolution.
NOTE: This function must not be called in the constructor (e.g. in order to
save the read result to the member variable earlier), since the parameter
library is updated at a later stage!
*/
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_, status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
&motorRecResolution); &motorRecResolution);
if (status == asynParamUndefined) { if (status == asynParamUndefined) {
@ -151,20 +103,31 @@ asynStatus pmacV3Axis::readConfig() {
} }
// Software limits and current position // Software limits and current position
snprintf(command, sizeof(command), "P%2.2d00 Q%2.2d10 Q%2.2d04 Q%2.2d06", snprintf(command, sizeof(command),
axisNo_, axisNo_, axisNo_, axisNo_); "P%2.2d00 Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22", axisNo_,
status = pC_->writeRead(axisNo_, command, response, 4); axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
nvals = sscanf(response, "%d %lf %lf %lf", &axStatus, &motorPosition, status = pC_->writeRead(axisNo_, command, response, 6);
&motorVelBase, &motorAccel); if (status != asynSuccess) {
if (nvals != 4) { return status;
}
nvals = sscanf(response, "%d %lf %lf %lf %lf %d", &axStatus, &motorPosition,
&motorVmax, &motorVelocity, &motorAccel, &acoDelay);
// The acoDelay is given in milliseconds -> Convert to seconds, rounded up
offsetMovTimeout_ = std::ceil(acoDelay / 1000.0);
// The PMAC electronic specifies the acceleration in m/s^2. Since we
// otherwise work with the base length mm, the acceleration is converted
// here to mm/s^2.
motorAccel = motorAccel * 1000;
if (nvals != 6) {
return pC_->errMsgCouldNotParseResponse(command, response, axisNo_, return pC_->errMsgCouldNotParseResponse(command, response, axisNo_,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
// Transform from motor to parameter library coordinates // Transform from motor to parameter library coordinates
motorPosition = motorPosition / motorRecResolution; motorPosition = motorPosition / motorRecResolution;
motorVelBase = motorVelBase / motorRecResolution;
motorAccel = motorAccel / motorRecResolution;
// Store these values in the parameter library // Store these values in the parameter library
status = pC_->setDoubleParam(axisNo_, pC_->motorPosition_, motorPosition); status = pC_->setDoubleParam(axisNo_, pC_->motorPosition_, motorPosition);
@ -173,24 +136,14 @@ asynStatus pmacV3Axis::readConfig() {
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
status = pC_->setDoubleParam(axisNo_, pC_->motorVelBase_, motorVelBase); // Write to the motor record fields
status = setVeloFields(motorVelocity, 0.0, motorVmax);
if (status != asynSuccess) { if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorVelBase_", return status;
__PRETTY_FUNCTION__, __LINE__);
} }
status = setAcclField(motorAccel);
status = pC_->setDoubleParam(axisNo_, pC_->motorAccel_, motorAccel);
if (status != asynSuccess) { if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorAccel_", return status;
__PRETTY_FUNCTION__, __LINE__);
}
// Set the initial enable based on the motor status value
status =
setIntegerParam(pC_->enableMotor_, (axStatus != -3 && axStatus != -5));
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "enableMotor_",
__PRETTY_FUNCTION__, __LINE__);
} }
// Update the parameter library immediately // Update the parameter library immediately
@ -210,7 +163,7 @@ asynStatus pmacV3Axis::readConfig() {
} }
// Perform the actual poll // Perform the actual poll
asynStatus pmacV3Axis::doPoll(bool *moving) { asynStatus pmacv3Axis::doPoll(bool *moving) {
// Return value for the poll // Return value for the poll
asynStatus poll_status = asynSuccess; asynStatus poll_status = asynSuccess;
@ -234,6 +187,7 @@ asynStatus pmacV3Axis::doPoll(bool *moving) {
int handshakePerformed = 0; int handshakePerformed = 0;
double highLimit = 0.0; double highLimit = 0.0;
double lowLimit = 0.0; double lowLimit = 0.0;
double limitsOffset = 0.0;
// ========================================================================= // =========================================================================
@ -287,7 +241,7 @@ asynStatus pmacV3Axis::doPoll(bool *moving) {
} }
// Transform from EPICS to motor coordinates (see comment in // Transform from EPICS to motor coordinates (see comment in
// pmacV3Axis::readConfig()) // pmacv3Axis::readConfig())
previousPosition = previousPosition * motorRecResolution; previousPosition = previousPosition * motorRecResolution;
// Query the axis status // Query the axis status
@ -318,8 +272,14 @@ asynStatus pmacV3Axis::doPoll(bool *moving) {
directly, but need to shrink them a bit. In this case, we're shrinking them directly, but need to shrink them a bit. In this case, we're shrinking them
by 0.1 mm or 0.1 degree (depending on the axis type) on both sides. by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
*/ */
highLimit = highLimit - offsetLimits_; pl_status =
lowLimit = lowLimit + offsetLimits_; pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset_, &limitsOffset);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLimitsOffset_",
__PRETTY_FUNCTION__, __LINE__);
}
highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset;
// Store the axis status // Store the axis status
axisStatus_ = axStatus; axisStatus_ = axStatus;
@ -445,11 +405,6 @@ asynStatus pmacV3Axis::doPoll(bool *moving) {
} }
} }
// Check and update the watchdog
if (checkMovTimeoutWatchdog(*moving) != asynSuccess) {
return asynError;
}
// Error handling // Error handling
switch (error) { switch (error) {
case 0: case 0:
@ -653,10 +608,10 @@ asynStatus pmacV3Axis::doPoll(bool *moving) {
} }
} }
pl_status = pl_status = setIntegerParam(pC_->motorEnableRBV_,
setIntegerParam(pC_->motorEnabled_, (axStatus != -3 && axStatus != -5)); (axStatus != -3 && axStatus != -5));
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnabled_", return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
@ -693,7 +648,7 @@ asynStatus pmacV3Axis::doPoll(bool *moving) {
} }
// Transform from motor to EPICS coordinates (see comment in // Transform from motor to EPICS coordinates (see comment in
// pmacV3Axis::readConfig()) // pmacv3Axis::readConfig())
currentPosition = currentPosition / motorRecResolution; currentPosition = currentPosition / motorRecResolution;
pl_status = setDoubleParam(pC_->motorPosition_, currentPosition); pl_status = setDoubleParam(pC_->motorPosition_, currentPosition);
@ -705,7 +660,7 @@ asynStatus pmacV3Axis::doPoll(bool *moving) {
return poll_status; return poll_status;
} }
asynStatus pmacV3Axis::doMove(double position, int relative, double minVelocity, asynStatus pmacv3Axis::doMove(double position, int relative, double minVelocity,
double maxVelocity, double acceleration) { double maxVelocity, double acceleration) {
// Status of read-write-operations of ASCII commands to the controller // Status of read-write-operations of ASCII commands to the controller
@ -716,14 +671,17 @@ asynStatus pmacV3Axis::doMove(double position, int relative, double minVelocity,
char command[pC_->MAXBUF_], response[pC_->MAXBUF_]; char command[pC_->MAXBUF_], response[pC_->MAXBUF_];
double motorCoordinatesPosition = 0.0; double motorCoordinatesPosition = 0.0;
int enabled = 0;
double motorRecResolution = 0.0; double motorRecResolution = 0.0;
double motorVelocity = 0.0;
int enabled = 0;
int motorCanSetSpeed = 0;
int writeOffset = 0;
// ========================================================================= // =========================================================================
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnabled_, &enabled); pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV_, &enabled);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnabled_", return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
@ -743,18 +701,43 @@ asynStatus pmacV3Axis::doMove(double position, int relative, double minVelocity,
// Convert from EPICS to user / motor units // Convert from EPICS to user / motor units
motorCoordinatesPosition = position * motorRecResolution; motorCoordinatesPosition = position * motorRecResolution;
motorVelocity = maxVelocity * motorRecResolution;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"%s => line %d:\nStart of axis %d to position %lf.\n", "%s => line %d:\nStart of axis %d to position %lf.\n",
__PRETTY_FUNCTION__, __LINE__, axisNo_, position); __PRETTY_FUNCTION__, __LINE__, axisNo_, position);
// Perform handshake, Set target position and start the move command // Check if the speed is allowed to be changed
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_,
&motorCanSetSpeed);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorCanSetSpeed_",
__PRETTY_FUNCTION__, __LINE__);
}
// Prepend the new motor speed, if the user is allowed to set the speed.
// Mind the " " (space) before the closing "", as the command created here
// is prepended to the one down below.
if (motorCanSetSpeed != 0) {
snprintf(command, sizeof(command), "Q%2.2d04=%lf ", axisNo_,
motorVelocity);
writeOffset = strlen(command);
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"%s => line %d:\nSetting speed of axis %d to %lf.\n",
__PRETTY_FUNCTION__, __LINE__, axisNo_, motorVelocity);
}
// Perform handshake, Set target position (and speed, if allowed) and start
// the move command
if (relative) { if (relative) {
snprintf(command, sizeof(command), "P%2.2d23=0 Q%2.2d02=%lf M%2.2d=2", snprintf(&command[writeOffset], sizeof(command) - writeOffset,
axisNo_, axisNo_, motorCoordinatesPosition, axisNo_); "P%2.2d23=0 Q%2.2d02=%lf M%2.2d=2", axisNo_, axisNo_,
motorCoordinatesPosition, axisNo_);
} else { } else {
snprintf(command, sizeof(command), "P%2.2d23=0 Q%2.2d01=%lf M%2.2d=1", snprintf(&command[writeOffset], sizeof(command) - writeOffset,
axisNo_, axisNo_, motorCoordinatesPosition, axisNo_); "P%2.2d23=0 Q%2.2d01=%lf M%2.2d=1", axisNo_, axisNo_,
motorCoordinatesPosition, axisNo_);
} }
// We don't expect an answer // We don't expect an answer
@ -788,7 +771,7 @@ asynStatus pmacV3Axis::doMove(double position, int relative, double minVelocity,
return rw_status; return rw_status;
} }
asynStatus pmacV3Axis::stop(double acceleration) { asynStatus pmacv3Axis::stop(double acceleration) {
// Status of read-write-operations of ASCII commands to the controller // Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess; asynStatus rw_status = asynSuccess;
@ -796,42 +779,32 @@ asynStatus pmacV3Axis::stop(double acceleration) {
// Status of parameter library operations // Status of parameter library operations
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
bool moving = false;
char command[pC_->MAXBUF_], response[pC_->MAXBUF_]; char command[pC_->MAXBUF_], response[pC_->MAXBUF_];
// ========================================================================= // =========================================================================
pl_status = doPoll(&moving); snprintf(command, sizeof(command), "M%2.2d=8", axisNo_);
if (pl_status != asynSuccess) { rw_status = pC_->writeRead(axisNo_, command, response, 0);
return pl_status;
}
if (moving) { if (rw_status != asynSuccess) {
// Only send a stop when actually moving asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
snprintf(command, sizeof(command), "M%2.2d=8", axisNo_); "%s => line %d:\nStopping the movement failed for axis %d.\n",
rw_status = pC_->writeRead(axisNo_, command, response, 0); __PRETTY_FUNCTION__, __LINE__, axisNo_);
if (rw_status != asynSuccess) { pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
asynPrint( if (pl_status != asynSuccess) {
pC_->pasynUserSelf, ASYN_TRACE_ERROR, return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
"%s => line %d:\nStopping the movement failed for axis %d.\n", __PRETTY_FUNCTION__, __LINE__);
__PRETTY_FUNCTION__, __LINE__, axisNo_);
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_",
__PRETTY_FUNCTION__, __LINE__);
}
} }
} }
return rw_status; return rw_status;
} }
/* /*
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 pmacV3Axis::doHome(double min_velocity, double max_velocity, asynStatus pmacv3Axis::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
@ -853,6 +826,7 @@ asynStatus pmacV3Axis::doHome(double min_velocity, double max_velocity,
// Only send the home command if the axis has an incremental encoder // Only send the home command if the axis has an incremental encoder
if (strcmp(response, IncrementalEncoder) == 0) { if (strcmp(response, IncrementalEncoder) == 0) {
snprintf(command, sizeof(command), "M%2.2d=9", axisNo_); snprintf(command, sizeof(command), "M%2.2d=9", axisNo_);
rw_status = pC_->writeRead(axisNo_, command, response, 0); rw_status = pC_->writeRead(axisNo_, command, response, 0);
if (rw_status != asynSuccess) { if (rw_status != asynSuccess) {
@ -885,7 +859,7 @@ asynStatus pmacV3Axis::doHome(double min_velocity, double max_velocity,
/* /*
Read the encoder type and update the parameter library accordingly Read the encoder type and update the parameter library accordingly
*/ */
asynStatus pmacV3Axis::readEncoderType() { asynStatus pmacv3Axis::readEncoderType() {
// Status of read-write-operations of ASCII commands to the controller // Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess; asynStatus rw_status = asynSuccess;
@ -908,12 +882,8 @@ asynStatus pmacV3Axis::readEncoderType() {
int reponse_length = strlen(response); int reponse_length = strlen(response);
if (reponse_length < 3) { if (reponse_length < 3) {
asynPrint( return pC_->errMsgCouldNotParseResponse(command, response, axisNo_,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, __PRETTY_FUNCTION__, __LINE__);
"%s => line %d:\nUnexpected reponse '%s' from axis %d on "
"controller %s while reading the encoder type. Aborting....\n",
__PRETTY_FUNCTION__, __LINE__, response, axisNo_, pC_->portName);
return asynError;
} }
// We are only interested in the last two digits and the last value in // We are only interested in the last two digits and the last value in
@ -954,7 +924,7 @@ the encoder as not all motors have breaks and they may start to move when
disabled. For that reason, we don't automatically disable the motors to run the disabled. For that reason, we don't automatically disable the motors to run the
command and instead require that the scientists first disable the motor. command and instead require that the scientists first disable the motor.
*/ */
asynStatus pmacV3Axis::rereadEncoder() { asynStatus pmacv3Axis::rereadEncoder() {
char command[pC_->MAXBUF_] = {0}; char command[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0}; char response[pC_->MAXBUF_] = {0};
char encoderType[pC_->MAXBUF_] = {0}; char encoderType[pC_->MAXBUF_] = {0};
@ -981,26 +951,15 @@ asynStatus pmacV3Axis::rereadEncoder() {
// Abort if the axis is incremental // Abort if the axis is incremental
if (strcmp(encoderType, IncrementalEncoder) == 1) { if (strcmp(encoderType, IncrementalEncoder) == 1) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING, return asynSuccess;
"%s => line %d:\nTrying to reread absolute encoder of "
"axis %d on controller %s, but it is a relative encoder.\n",
__PRETTY_FUNCTION__, __LINE__, axisNo_, pC_->portName);
pl_status = setStringParam(pC_->motorMessageText_,
"Cannot reread an incremental encoder.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__);
}
return asynError;
} }
// Check if the axis is disabled. If not, inform the user that this // Check if the axis is disabled. If not, inform the user that this
// is necessary // is necessary
int enabled = 0; int enabled = 0;
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnabled_, &enabled); pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV_, &enabled);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnabled_", return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
@ -1043,7 +1002,7 @@ asynStatus pmacV3Axis::rereadEncoder() {
return asynSuccess; return asynSuccess;
} }
asynStatus pmacV3Axis::enable(bool on) { asynStatus pmacv3Axis::enable(bool on) {
int timeout_enable_disable = 2; int timeout_enable_disable = 2;
char command[pC_->MAXBUF_], response[pC_->MAXBUF_]; char command[pC_->MAXBUF_], response[pC_->MAXBUF_];
@ -1056,14 +1015,17 @@ asynStatus pmacV3Axis::enable(bool on) {
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
bool moving = false; bool moving = false;
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 // command and inform the user. We check the last known status of the axis
doPoll(&moving); // Muss gehen wenn Status -6 // instead of "moving", since status -6 is also moving, but the motor can
if (moving) { // actually be disabled in this state!
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING, if (axisStatus_ == 1 || axisStatus_ == 2 || axisStatus_ == 3 ||
axisStatus_ == 4 || axisStatus_ == 5) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nAxis %d is not idle and can therefore not " "%s => line %d:\nAxis %d is not idle and can therefore not "
"be enabled / disabled.\n", "be enabled / disabled.\n",
__PRETTY_FUNCTION__, __LINE__, axisNo_); __PRETTY_FUNCTION__, __LINE__, axisNo_);
@ -1076,13 +1038,6 @@ asynStatus pmacV3Axis::enable(bool on) {
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
// Reset the value in the param lib.
pl_status = setIntegerParam(pC_->enableMotor_, 1);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "enableMotor_",
__PRETTY_FUNCTION__, __LINE__);
}
return asynError; return asynError;
} }
@ -1096,6 +1051,14 @@ asynStatus pmacV3Axis::enable(bool on) {
return asynSuccess; return asynSuccess;
} }
// Reread the encoder, if the axis is going to be enabled
if (on != 0) {
rw_status = rereadEncoder();
if (rw_status != asynSuccess) {
return rw_status;
}
}
// Enable / disable the axis if it is not moving // Enable / disable the axis if it is not moving
snprintf(command, sizeof(command), "M%2.2d14=%d", axisNo_, on); snprintf(command, sizeof(command), "M%2.2d14=%d", axisNo_, on);
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
@ -1111,7 +1074,6 @@ asynStatus pmacV3Axis::enable(bool on) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
rw_status = pC_->writeRead(axisNo_, command, response, 0); rw_status = pC_->writeRead(axisNo_, command, response, 0);
if (rw_status != asynSuccess) { if (rw_status != asynSuccess) {
return rw_status; return rw_status;
@ -1161,3 +1123,8 @@ asynStatus pmacV3Axis::enable(bool on) {
} }
return asynError; return asynError;
} }
asynStatus pmacv3Axis::isEnabled(bool *on) {
*on = (axisStatus_ != -3 && axisStatus_ != -5);
return asynSuccess;
}

View File

@ -1,33 +1,27 @@
#ifndef pmacV3AXIS_H #ifndef pmacv3AXIS_H
#define pmacV3AXIS_H #define pmacv3AXIS_H
#include "sinqAxis.h" #include "sinqAxis.h"
// Forward declaration of the controller class to resolve the cyclic dependency // Forward declaration of the controller class to resolve the cyclic dependency
// between C804Controller.h and C804Axis.h. See // between C804Controller.h and C804Axis.h. See
// https://en.cppreference.com/w/cpp/language/class. // https://en.cppreference.com/w/cpp/language/class.
class pmacV3Controller; class pmacv3Controller;
class pmacV3Axis : public sinqAxis { class pmacv3Axis : public sinqAxis {
public: public:
/** /**
* @brief Construct a new pmacV3Axis * @brief Construct a new pmacv3Axis
* *
* @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 offsetLimit The high and low limits of the axis are read
* out directly from the MCU. However, since the axis might slightly
"overshoot" when moving to a position next to the limits, the MCU might go
into the "limits hit" error state. To prevent this, this value allows adding
a small offset, which is subtracted from the high limit and added to the
low limit.
*/ */
pmacV3Axis(pmacV3Controller *pController, int axisNo, double offsetLimit); pmacv3Axis(pmacv3Controller *pController, int axisNo);
/** /**
* @brief Destroy the pmacV3Axis * @brief Destroy the pmacv3Axis
* *
*/ */
virtual ~pmacV3Axis(); virtual ~pmacv3Axis();
/** /**
* @brief Implementation of the `stop` function from asynMotorAxis * @brief Implementation of the `stop` function from asynMotorAxis
@ -94,6 +88,15 @@ class pmacV3Axis : public sinqAxis {
*/ */
asynStatus enable(bool on); asynStatus enable(bool on);
/**
* @brief EThis function sets "on" to true, if the motor is enabled, and to
* false otherwise
*
* @param on
* @return asynStatus
*/
asynStatus isEnabled(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
* the MCU and store the information in the PV ENCODER_TYPE. * the MCU and store the information in the PV ENCODER_TYPE.
@ -110,7 +113,7 @@ class pmacV3Axis : public sinqAxis {
asynStatus rereadEncoder(); asynStatus rereadEncoder();
protected: protected:
pmacV3Controller *pC_; pmacv3Controller *pC_;
asynStatus readConfig(); asynStatus readConfig();
bool initial_poll_; bool initial_poll_;
@ -119,13 +122,9 @@ class pmacV3Axis : public sinqAxis {
// The axis status is used when enabling / disabling the motor // The axis status is used when enabling / disabling the motor
int axisStatus_; int axisStatus_;
/*
Stores the constructor input offsetLimits
*/
double offsetLimits_;
private: private:
friend class pmacV3Controller; friend class pmacv3Controller;
}; };
#endif #endif

View File

@ -1,8 +1,10 @@
// Needed to use strcpy_s from string.h
#define __STDC_WANT_LIB_EXT1__ 1
#include "pmacV3Controller.h" #include "pmacv3Controller.h"
#include "asynMotorController.h" #include "asynMotorController.h"
#include "asynOctetSyncIO.h" #include "asynOctetSyncIO.h"
#include "pmacV3Axis.h" #include "pmacv3Axis.h"
#include <epicsExport.h> #include <epicsExport.h>
#include <errlog.h> #include <errlog.h>
#include <iocsh.h> #include <iocsh.h>
@ -12,7 +14,25 @@
#include <unistd.h> #include <unistd.h>
/** /**
* @brief Construct a new pmacV3Controller::pmacV3Controller object * @brief Copy src into dst and replace all carriage returns with spaces
*
* @param dst Buffer for the modified string
* @param src Original string
*/
void adjustResponseForPrint(char *dst, const char *src) {
// Needed to use strcpy_s from string.h
#ifdef __STDC_LIB_EXT1__
strcpy_s(dst, src);
for (size_t i = 0; i < strlen(dst); i++) {
if (dst[i] == '\r') {
dst[i] = '_';
}
}
#endif
}
/**
* @brief Construct a new pmacv3Controller::pmacv3Controller object
* *
* @param portName See documentation of sinqController * @param portName See documentation of sinqController
* @param ipPortConfigName See documentation of sinqController * @param ipPortConfigName See documentation of sinqController
@ -23,7 +43,7 @@
* is declared in writeRead (in seconds) * is declared in writeRead (in seconds)
* @param extraParams See documentation of sinqController * @param extraParams See documentation of sinqController
*/ */
pmacV3Controller::pmacV3Controller(const char *portName, pmacv3Controller::pmacv3Controller(const char *portName,
const char *ipPortConfigName, int numAxes, const char *ipPortConfigName, int numAxes,
double movingPollPeriod, double movingPollPeriod,
double idlePollPeriod, double comTimeout) double idlePollPeriod, double comTimeout)
@ -31,16 +51,12 @@ pmacV3Controller::pmacV3Controller(const char *portName,
portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod, portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod,
/* /*
The following parameter library entries are added in this driver: The following parameter library entries are added in this driver:
- ENABLE_AXIS
- AXIS_ENABLED
- ENCODER_TYPE - ENCODER_TYPE
- REREAD_ENCODER_POSITION - REREAD_ENCODER_POSITION
- REREAD_ENCODER_POSITION_RBV
- READ_CONFIG - READ_CONFIG
- MOTOR_HIGH_LIMIT_FROM_DRIVER - ACCEL_FROM_DRIVER
- MOTOR_LOW_LIMIT_FROM_DRIVER
*/ */
8) 4)
{ {
@ -69,24 +85,6 @@ pmacV3Controller::pmacV3Controller(const char *portName,
// ========================================================================= // =========================================================================
// Create additional parameter library entries // Create additional parameter library entries
status = createParam("ENABLE_AXIS", asynParamInt32, &enableMotor_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status = createParam("AXIS_ENABLED", asynParamInt32, &motorEnabled_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status = createParam("ENCODER_TYPE", asynParamOctet, &encoderType_); status = createParam("ENCODER_TYPE", asynParamOctet, &encoderType_);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@ -106,16 +104,6 @@ pmacV3Controller::pmacV3Controller(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("REREAD_ENCODER_POSITION_RBV", asynParamInt32,
&rereadEncoderPositionRBV_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status = createParam("READ_CONFIG", asynParamInt32, &readConfig_); status = createParam("READ_CONFIG", asynParamInt32, &readConfig_);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@ -125,31 +113,6 @@ pmacV3Controller::pmacV3Controller(const char *portName,
exit(-1); exit(-1);
} }
/*
We need to introduce 2 new parameters in order to write the limits from the
driver to the EPICS record. See the comment in pmacV3Controller.h next to
the declaration of motorHighLimitFromDriver_.
*/
status = createParam("MOTOR_HIGH_LIMIT_FROM_DRIVER", asynParamFloat64,
&motorHighLimitFromDriver_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
status = createParam("MOTOR_LOW_LIMIT_FROM_DRIVER", asynParamFloat64,
&motorLowLimitFromDriver_);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
"with %s).\nTerminating IOC",
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
exit(-1);
}
/* /*
Define the end-of-string of a message coming from the device to EPICS. Define the end-of-string of a message coming from the device to EPICS.
It is not necessary to append a terminator to outgoing messages, since It is not necessary to append a terminator to outgoing messages, since
@ -187,21 +150,21 @@ 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
error is emitted. error is emitted.
*/ */
pmacV3Axis *pmacV3Controller::getAxis(asynUser *pasynUser) { pmacv3Axis *pmacv3Controller::getAxis(asynUser *pasynUser) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(pasynUser); asynMotorAxis *asynAxis = asynMotorController::getAxis(pasynUser);
return pmacV3Controller::castToAxis(asynAxis); return pmacv3Controller::castToAxis(asynAxis);
} }
/* /*
Access one of the axes of the controller via the axis index. Access one of the axes of the controller via the axis index.
If the axis does not exist or is not a Axis, the function must return Null If the axis does not exist or is not a Axis, the function must return Null
*/ */
pmacV3Axis *pmacV3Controller::getAxis(int axisNo) { pmacv3Axis *pmacv3Controller::getAxis(int axisNo) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(axisNo); asynMotorAxis *asynAxis = asynMotorController::getAxis(axisNo);
return pmacV3Controller::castToAxis(asynAxis); return pmacv3Controller::castToAxis(asynAxis);
} }
pmacV3Axis *pmacV3Controller::castToAxis(asynMotorAxis *asynAxis) { pmacv3Axis *pmacv3Controller::castToAxis(asynMotorAxis *asynAxis) {
// ========================================================================= // =========================================================================
@ -212,24 +175,25 @@ pmacV3Axis *pmacV3Controller::castToAxis(asynMotorAxis *asynAxis) {
// Here, an error is emitted since asyn_axis is not a nullptr but also not // Here, an error is emitted since asyn_axis is not a nullptr but also not
// an instance of Axis // an instance of Axis
pmacV3Axis *axis = dynamic_cast<pmacV3Axis *>(asynAxis); pmacv3Axis *axis = dynamic_cast<pmacv3Axis *>(asynAxis);
if (axis == nullptr) { if (axis == nullptr) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nAxis %d is not an instance of pmacV3Axis", "%s => line %d:\nAxis %d is not an instance of pmacv3Axis",
__PRETTY_FUNCTION__, __LINE__, axis->axisNo_); __PRETTY_FUNCTION__, __LINE__, axis->axisNo_);
} }
return axis; return axis;
} }
asynStatus pmacV3Controller::writeRead(int axisNo, const char *command, asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
char *response, char *response,
int numExpectedResponses) { int numExpectedResponses) {
// Definition of local variables. // Definition of local variables.
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
asynStatus pl_status = asynSuccess; asynStatus pl_status = asynSuccess;
char full_command[MAXBUF_] = {0}; char fullCommand[MAXBUF_] = {0};
char user_message[MAXBUF_] = {0}; char drvMessageText[MAXBUF_] = {0};
char modResponse[MAXBUF_] = {0};
int motorStatusProblem = 0; int motorStatusProblem = 0;
int numReceivedResponses = 0; int numReceivedResponses = 0;
@ -247,14 +211,14 @@ asynStatus pmacV3Controller::writeRead(int axisNo, const char *command,
// ========================================================================= // =========================================================================
pmacV3Axis *axis = getAxis(axisNo); pmacv3Axis *axis = getAxis(axisNo);
if (axis == nullptr) { if (axis == nullptr) {
// We already did the error logging directly in getAxis // We already did the error logging directly in getAxis
return asynError; return asynError;
} }
/* /*
The message protocol of the pmacV3 used at PSI looks as follows (all The message protocol of the pmacv3 used at PSI looks as follows (all
characters immediately following each other without a newline): characters immediately following each other without a newline):
0x40 (ASCII value of @) -> Request for download 0x40 (ASCII value of @) -> Request for download
0xBF (ASCII value of ¿) -> Select mode "get_response" 0xBF (ASCII value of ¿) -> Select mode "get_response"
@ -268,7 +232,7 @@ asynStatus pmacV3Controller::writeRead(int axisNo, const char *command,
this protocol encodes the message length at the beginning. See Turbo PMAC this protocol encodes the message length at the beginning. See Turbo PMAC
User Manual, page 418 in VR_PMAC_GETRESPONSE User Manual, page 418 in VR_PMAC_GETRESPONSE
The message has to be build manually into the buffer full_command, since it The message has to be build manually into the buffer fullCommand, since it
contains NULL terminators in its middle, therefore the string manipulation contains NULL terminators in its middle, therefore the string manipulation
methods of C don't work. methods of C don't work.
*/ */
@ -278,20 +242,20 @@ asynStatus pmacV3Controller::writeRead(int axisNo, const char *command,
strlen(command) + 1; // +1 because of the appended /r strlen(command) + 1; // +1 because of the appended /r
const int offset = 8; const int offset = 8;
// Positions 2 to 6 must have the value 0. Since full_command is initialized // Positions 2 to 6 must have the value 0. Since fullCommand is initialized
// as an array of zeros, we don't need to set these bits manually. // as an array of zeros, we don't need to set these bits manually.
full_command[0] = '\x40'; fullCommand[0] = '\x40';
full_command[1] = '\xBF'; fullCommand[1] = '\xBF';
full_command[7] = commandLength; fullCommand[7] = commandLength;
snprintf((char *)full_command + offset, MAXBUF_ - offset, "%s\r", command); snprintf((char *)fullCommand + offset, MAXBUF_ - offset, "%s\r", command);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"%s => line %d:\nSending command %s", __PRETTY_FUNCTION__, "%s => line %d:\nSending command %s", __PRETTY_FUNCTION__,
__LINE__, full_command); __LINE__, fullCommand);
// Perform the actual writeRead // Perform the actual writeRead
status = pasynOctetSyncIO->writeRead( status = pasynOctetSyncIO->writeRead(
lowLevelPortUser_, full_command, commandLength + offset, response, lowLevelPortUser_, fullCommand, commandLength + offset, response,
MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn, &eomReason); MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn, &eomReason);
/* /*
@ -327,7 +291,7 @@ asynStatus pmacV3Controller::writeRead(int axisNo, const char *command,
if (status == asynSuccess) { if (status == asynSuccess) {
// If flushing the MCU succeded, try to send the command again // If flushing the MCU succeded, try to send the command again
status = pasynOctetSyncIO->writeRead( status = pasynOctetSyncIO->writeRead(
lowLevelPortUser_, full_command, commandLength + offset, lowLevelPortUser_, fullCommand, commandLength + offset,
response, MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn, response, MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn,
&eomReason); &eomReason);
@ -342,15 +306,18 @@ asynStatus pmacV3Controller::writeRead(int axisNo, const char *command,
// Second check: If this fails, give up and propagate the error. // Second check: If this fails, give up and propagate the error.
if (numExpectedResponses != numReceivedResponses) { if (numExpectedResponses != numReceivedResponses) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR, adjustResponseForPrint(modResponse, response);
"%s => line %d:\nUnexpected response %s for command %s\n", asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
__PRETTY_FUNCTION__, __LINE__, response, command); "%s => line %d:\nUnexpected response %s (_ are "
snprintf(user_message, sizeof(user_message), "carriage returns) for command %s\n",
"Received unexpected response %s for command %s. " __PRETTY_FUNCTION__, __LINE__, modResponse, command);
snprintf(drvMessageText, sizeof(drvMessageText),
"Received unexpected response %s (_ are "
"carriage returns) for command %s. "
"Please call the support", "Please call the support",
response, command); modResponse, command);
pl_status = setStringParam(motorMessageText_, user_message); pl_status = setStringParam(motorMessageText_, drvMessageText);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorMessageText_", return paramLibAccessFailed(pl_status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
@ -366,23 +333,24 @@ asynStatus pmacV3Controller::writeRead(int axisNo, const char *command,
} }
// Create custom error messages for different failure modes // Create custom error messages for different failure modes
if (strlen(user_message) == 0) { if (strlen(drvMessageText) == 0) {
switch (status) { switch (status) {
case asynSuccess: case asynSuccess:
break; // Communicate nothing break; // Communicate nothing
case asynTimeout: case asynTimeout:
snprintf(user_message, sizeof(user_message), snprintf(drvMessageText, sizeof(drvMessageText),
"connection timeout for axis %d", axisNo); "connection timeout for axis %d", axisNo);
break; break;
case asynDisconnected: case asynDisconnected:
snprintf(user_message, sizeof(user_message), snprintf(drvMessageText, sizeof(drvMessageText),
"axis is not connected"); "axis is not connected");
break; break;
case asynDisabled: case asynDisabled:
snprintf(user_message, sizeof(user_message), "axis is disabled"); snprintf(drvMessageText, sizeof(drvMessageText),
"axis is disabled");
break; break;
default: default:
snprintf(user_message, sizeof(user_message), snprintf(drvMessageText, sizeof(drvMessageText),
"Communication failed (%s)", stringifyAsynStatus(status)); "Communication failed (%s)", stringifyAsynStatus(status));
break; break;
} }
@ -403,7 +371,7 @@ asynStatus pmacV3Controller::writeRead(int axisNo, const char *command,
if (motorStatusProblem == 0) { if (motorStatusProblem == 0) {
pl_status = pl_status =
axis->setStringParam(this->motorMessageText_, user_message); axis->setStringParam(this->motorMessageText_, drvMessageText);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorMessageText_", return paramLibAccessFailed(pl_status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
@ -413,16 +381,18 @@ asynStatus pmacV3Controller::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) {
asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER, asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER,
"%s => line %d:\nDevice response: %s\n", __PRETTY_FUNCTION__, "%s => line %d:\nDevice response: %s (_ are "
__LINE__, response); "carriage returns)\n",
__PRETTY_FUNCTION__, __LINE__, modResponse);
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0); pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
} else { } else {
if (status == asynSuccess) { if (status == asynSuccess) {
asynPrint( asynPrint(
lowLevelPortUser_, ASYN_TRACE_ERROR, lowLevelPortUser_, ASYN_TRACE_ERROR,
"%s => line %d:\nCommunication failed for command %s (%s)\n", "%s => line %d:\nCommunication failed for command %s (%s)\n",
__PRETTY_FUNCTION__, __LINE__, full_command, __PRETTY_FUNCTION__, __LINE__, fullCommand,
stringifyAsynStatus(status)); stringifyAsynStatus(status));
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 1); pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 1);
} }
@ -435,82 +405,34 @@ asynStatus pmacV3Controller::writeRead(int axisNo, const char *command,
return asynSuccess; return asynSuccess;
} }
asynStatus pmacV3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) { asynStatus pmacv3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) {
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "enabling %d", value); int function = pasynUser->reason;
pmacV3Axis *axis = getAxis(pasynUser); // =====================================================================
pmacv3Axis *axis = getAxis(pasynUser);
if (axis == nullptr) { if (axis == nullptr) {
// We already did the error logging directly in getAxis // We already did the error logging directly in getAxis
return asynError; return asynError;
} }
// Handle custom PVs // Handle custom PVs
if (pasynUser->reason == enableMotor_) { if (function == rereadEncoderPosition_) {
return axis->enable(value != 0);
} else if (pasynUser->reason == rereadEncoderPosition_) {
return axis->rereadEncoder(); return axis->rereadEncoder();
} else if (function == readConfig_) {
return axis->readConfig();
} else { } else {
return asynMotorController::writeInt32(pasynUser, value); return sinqController::writeInt32(pasynUser, value);
} }
} }
/* asynStatus pmacv3Controller::errMsgCouldNotParseResponse(
Overloaded from asynMotorController because the special cases "motor const char *command, const char *response, int axisNo,
enabling" and "rereading the encoder" must be covered. const char *functionName, int lineNumber) {
*/ char modifiedResponse[MAXBUF_] = {0};
asynStatus pmacV3Controller::readInt32(asynUser *pasynUser, epicsInt32 *value) { adjustResponseForPrint(modifiedResponse, response);
return sinqController::errMsgCouldNotParseResponse(
int function = pasynUser->reason; command, modifiedResponse, axisNo, functionName, lineNumber);
asynStatus status = asynError;
// =====================================================================
pmacV3Axis *axis = getAxis(pasynUser);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
}
if (function == rereadEncoderPositionRBV_) {
// Readback value for rereadEncoderPosition
status = getIntegerParam(axis->axisNo_, rereadEncoderPosition_, value);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "rereadEncoderPosition_",
__PRETTY_FUNCTION__, __LINE__);
}
status =
setIntegerParam(axis->axisNo_, rereadEncoderPositionRBV_, *value);
if (status != asynSuccess) {
return paramLibAccessFailed(status, "rereadEncoderPositionRBV_",
__PRETTY_FUNCTION__, __LINE__);
}
// Update the PVs from the parameter library
return callParamCallbacks();
} else if (function == motorEnabled_) {
char command[MAXBUF_], response[MAXBUF_];
int axStatus = 0;
// Query the motor status from the affected axis
snprintf(command, sizeof(command), "P%2.2d00", axis->axisNo_);
status = writeRead(axis->axisNo_, command, response, 1);
if (status != asynSuccess) {
return status;
}
int nvals = sscanf(response, "%d", &axStatus);
if (nvals != 1) {
return errMsgCouldNotParseResponse(command, response, axis->axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
// Update the parameter library
return setIntegerParam(motorEnabled_,
(axStatus != -3 && axStatus != -5));
} else {
return asynMotorController::readInt32(pasynUser, value);
}
} }
/*************************************************************************************/ /*************************************************************************************/
@ -520,10 +442,10 @@ asynStatus pmacV3Controller::readInt32(asynUser *pasynUser, epicsInt32 *value) {
extern "C" { extern "C" {
/* /*
C wrapper for the controller constructor. Please refer to the pmacV3Controller C wrapper for the controller constructor. Please refer to the pmacv3Controller
constructor documentation. constructor documentation.
*/ */
asynStatus pmacV3CreateController(const char *portName, asynStatus pmacv3CreateController(const char *portName,
const char *lowLevelPortName, int numAxes, const char *lowLevelPortName, int numAxes,
double movingPollPeriod, double movingPollPeriod,
double idlePollPeriod, double comTimeout) { double idlePollPeriod, double comTimeout) {
@ -538,20 +460,19 @@ asynStatus pmacV3CreateController(const char *portName,
*/ */
#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"
pmacV3Controller *pController = pmacv3Controller *pController =
new pmacV3Controller(portName, lowLevelPortName, numAxes, new pmacv3Controller(portName, lowLevelPortName, numAxes,
movingPollPeriod, idlePollPeriod, comTimeout); movingPollPeriod, idlePollPeriod, comTimeout);
return asynSuccess; return asynSuccess;
} }
/* /*
C wrapper for the axis constructor. Please refer to the pmacV3Axis constructor C wrapper for the axis constructor. Please refer to the pmacv3Axis constructor
documentation. The controller is read from the portName. documentation. The controller is read from the portName.
*/ */
asynStatus pmacV3CreateAxis(const char *portName, int axis, asynStatus pmacv3CreateAxis(const char *portName, int axis) {
double offsetLimits) { pmacv3Axis *pAxis;
pmacV3Axis *pAxis;
/* /*
findAsynPortDriver is a asyn library FFI function which uses the C ABI. findAsynPortDriver is a asyn library FFI function which uses the C ABI.
@ -582,10 +503,10 @@ asynStatus pmacV3CreateAxis(const char *portName, int axis,
asynPortDriver *apd = (asynPortDriver *)(ptr); asynPortDriver *apd = (asynPortDriver *)(ptr);
// Safe downcast // Safe downcast
pmacV3Controller *pC = dynamic_cast<pmacV3Controller *>(apd); pmacv3Controller *pC = dynamic_cast<pmacv3Controller *>(apd);
if (pC == nullptr) { if (pC == nullptr) {
errlogPrintf( errlogPrintf(
"%s => line %d:\ncontroller on port %s is not a pmacV3Controller.", "%s => line %d:\ncontroller on port %s is not a pmacv3Controller.",
__PRETTY_FUNCTION__, __LINE__, portName); __PRETTY_FUNCTION__, __LINE__, portName);
return asynError; return asynError;
} }
@ -605,7 +526,7 @@ asynStatus pmacV3CreateAxis(const char *portName, int axis,
*/ */
#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"
pAxis = new pmacV3Axis(pC, axis, offsetLimits); pAxis = new pmacv3Axis(pC, axis);
// Allow manipulation of the controller again // Allow manipulation of the controller again
pC->unlock(); pC->unlock();
@ -627,9 +548,9 @@ types and then providing "factory" functions
(configCreateControllerCallFunc). These factory functions are used to (configCreateControllerCallFunc). These factory functions are used to
register the constructors during compilation. register the constructors during compilation.
*/ */
static const iocshArg CreateControllerArg0 = {"Controller port name", static const iocshArg CreateControllerArg0 = {"Controller name (e.g. mcu1)",
iocshArgString}; iocshArgString};
static const iocshArg CreateControllerArg1 = {"Low level port name", static const iocshArg CreateControllerArg1 = {"Asyn IP port name (e.g. pmcu1)",
iocshArgString}; iocshArgString};
static const iocshArg CreateControllerArg2 = {"Number of axes", iocshArgInt}; static const iocshArg CreateControllerArg2 = {"Number of axes", iocshArgInt};
static const iocshArg CreateControllerArg3 = {"Moving poll rate (s)", static const iocshArg CreateControllerArg3 = {"Moving poll rate (s)",
@ -641,10 +562,10 @@ static const iocshArg CreateControllerArg5 = {"Communication timeout (s)",
static const iocshArg *const CreateControllerArgs[] = { static const iocshArg *const CreateControllerArgs[] = {
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2, &CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5}; &CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
static const iocshFuncDef configPmacV3CreateController = { static const iocshFuncDef configPmacV3CreateController = {"pmacv3Controller", 6,
"revisedPmacV3CreateController", 6, CreateControllerArgs}; CreateControllerArgs};
static void configPmacV3CreateControllerCallFunc(const iocshArgBuf *args) { static void configPmacV3CreateControllerCallFunc(const iocshArgBuf *args) {
pmacV3CreateController(args[0].sval, args[1].sval, args[2].ival, pmacv3CreateController(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);
} }
@ -652,26 +573,25 @@ static void configPmacV3CreateControllerCallFunc(const iocshArgBuf *args) {
Same procedure as for the CreateController function, but for the axis Same procedure as for the CreateController function, but for the axis
itself. itself.
*/ */
static const iocshArg CreateAxisArg0 = {"Controller port name", iocshArgString}; static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mcu1)",
iocshArgString};
static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt}; static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg CreateAxisArg2 = { static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
"Offset for the MCU limits in mm or degree", iocshArgDouble}; &CreateAxisArg1};
static const iocshArg *const CreateAxisArgs[] = { static const iocshFuncDef configPmacV3CreateAxis = {"pmacv3Axis", 2,
&CreateAxisArg0, &CreateAxisArg1, &CreateAxisArg2}; CreateAxisArgs};
static const iocshFuncDef configPmacV3CreateAxis = {"revisedPmacV3CreateAxis",
3, CreateAxisArgs};
static void configPmacV3CreateAxisCallFunc(const iocshArgBuf *args) { static void configPmacV3CreateAxisCallFunc(const iocshArgBuf *args) {
pmacV3CreateAxis(args[0].sval, args[1].ival, args[2].dval); pmacv3CreateAxis(args[0].sval, args[1].ival);
} }
// This function is made known to EPICS in pmacV3.dbd and is called by EPICS // This function is made known to EPICS in pmacv3.dbd and is called by EPICS
// in order to register both functions in the IOC shell // in order to register both functions in the IOC shell
static void pmacV3Register(void) { static void pmacv3Register(void) {
iocshRegister(&configPmacV3CreateController, iocshRegister(&configPmacV3CreateController,
configPmacV3CreateControllerCallFunc); configPmacV3CreateControllerCallFunc);
iocshRegister(&configPmacV3CreateAxis, configPmacV3CreateAxisCallFunc); iocshRegister(&configPmacV3CreateAxis, configPmacV3CreateAxisCallFunc);
} }
epicsExportRegistrar(pmacV3Register); epicsExportRegistrar(pmacv3Register);
#endif #endif

View File

@ -1,25 +1,25 @@
/******************************************** /********************************************
* pmacV3Controller.h * pmacv3Controller.h
* *
* PMAC V3 controller driver based on the asynMotorController class * PMAC V3 controller driver based on the asynMotorController class
* *
* Stefan Mathis, September 2024 * Stefan Mathis, September 2024
********************************************/ ********************************************/
#ifndef pmacV3Controller_H #ifndef pmacv3Controller_H
#define pmacV3Controller_H #define pmacv3Controller_H
#include "pmacV3Axis.h" #include "pmacv3Axis.h"
#include "sinqAxis.h" #include "sinqAxis.h"
#include "sinqController.h" #include "sinqController.h"
#define IncrementalEncoder "Incremental encoder" #define IncrementalEncoder "Incremental encoder"
#define AbsoluteEncoder "Absolute encoder" #define AbsoluteEncoder "Absolute encoder"
class pmacV3Controller : public sinqController { class pmacv3Controller : public sinqController {
public: public:
/** /**
* @brief Construct a new pmacV3Controller object * @brief Construct a new pmacv3Controller object
* *
* @param portName See sinqController constructor * @param portName See sinqController constructor
* @param ipPortConfigName See sinqController constructor * @param ipPortConfigName See sinqController constructor
@ -30,7 +30,7 @@ class pmacV3Controller : public sinqController {
the underlying asynOctetSyncIO interface waits for a response until this the underlying asynOctetSyncIO interface waits for a response until this
time (in seconds) has passed, then it declares a timeout. time (in seconds) has passed, then it declares a timeout.
*/ */
pmacV3Controller(const char *portName, const char *ipPortConfigName, pmacv3Controller(const char *portName, const char *ipPortConfigName,
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout); double idlePollPeriod, double comTimeout);
@ -38,23 +38,22 @@ class pmacV3Controller : public sinqController {
* @brief Get the axis object * @brief Get the axis object
* *
* @param pasynUser Specify the axis via the asynUser * @param pasynUser Specify the axis via the asynUser
* @return pmacV3Axis* If no axis could be found, this is a nullptr * @return pmacv3Axis* If no axis could be found, this is a nullptr
*/ */
pmacV3Axis *getAxis(asynUser *pasynUser); pmacv3Axis *getAxis(asynUser *pasynUser);
/** /**
* @brief Get the axis object * @brief Get the axis object
* *
* @param axisNo Specify the axis via its index * @param axisNo Specify the axis via its index
* @return pmacV3Axis* If no axis could be found, this is a nullptr * @return pmacv3Axis* If no axis could be found, this is a nullptr
*/ */
pmacV3Axis *getAxis(int axisNo); pmacv3Axis *getAxis(int axisNo);
/** /**
* @brief Overloaded function of asynMotorController * @brief Overloaded function of sinqController
* *
* The function is overloaded to allow enabling / disabling the motor and * The function is overloaded to allow rereading the encoder and config.
* rereading the encoder.
* *
* @param pasynUser Specify the axis via the asynUser * @param pasynUser Specify the axis via the asynUser
* @param value New value * @param value New value
@ -62,18 +61,6 @@ class pmacV3Controller : public sinqController {
*/ */
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value); asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
/**
* @brief Overloaded function of asynMotorController
*
* The function is overloaded to get readback values for the enabling /
* disabling status and the encoder.
*
* @param pasynUser Specify the axis via the asynUser
* @param value Read-out value
* @return asynStatus
*/
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
protected: protected:
asynUser *lowLevelPortUser_; asynUser *lowLevelPortUser_;
@ -97,13 +84,36 @@ class pmacV3Controller : public sinqController {
int numExpectedResponses); int numExpectedResponses);
/** /**
* @brief Save cast of the given asynAxis pointer to a pmacV3Axis pointer. * @brief Save cast of the given asynAxis pointer to a pmacv3Axis pointer.
* If the cast fails, this function returns a nullptr. * If the cast fails, this function returns a nullptr.
* *
* @param asynAxis * @param asynAxis
* @return pmacV3Axis* * @return pmacv3Axis*
*/ */
pmacV3Axis *castToAxis(asynMotorAxis *asynAxis); pmacv3Axis *castToAxis(asynMotorAxis *asynAxis);
/**
* @brief Specialized version of sinqController::errMsgCouldNotParseResponse
* for pmacv3
*
* This is an overloaded version of
* sinqController::errMsgCouldNotParseResponse which calls
* adjustResponseForLogging on response before handing it over to
* sinqController::errMsgCouldNotParseResponse.
*
* @param command Command which led to the unparseable message
* @param response Response which wasn't parseable
* @param axisNo_ Axis where the problem occurred
* @param functionName Name of the caller function. It is recommended
to use a macro, e.g. __func__ or __PRETTY_FUNCTION__.
* @param lineNumber Source code line where this function is
called. It is recommended to use a macro, e.g. __LINE__.
* @return asynStatus Returns asynError.
*/
asynStatus errMsgCouldNotParseResponse(const char *command,
const char *response, int axisNo_,
const char *functionName,
int lineNumber);
private: private:
// Set the maximum buffer size. This is an empirical value which must be // Set the maximum buffer size. This is an empirical value which must be
@ -117,24 +127,11 @@ class pmacV3Controller : public sinqController {
double comTimeout_; double comTimeout_;
// Indices of additional PVs // Indices of additional PVs
int enableMotor_;
int motorEnabled_;
int rereadEncoderPosition_; int rereadEncoderPosition_;
int rereadEncoderPositionRBV_;
int readConfig_; int readConfig_;
int encoderType_; int encoderType_;
/* friend class pmacv3Axis;
These parameters are here to write the high and low limits from the MCU to
the EPICS motor record. Using motorHighLimit_ / motorLowLimit_ does not
work: https://epics.anl.gov/tech-talk/2023/msg00576.php.
Therefore, some additional records are introduced which read from these
parameters and write into the motor record. See the sinq_asyn_motor.db file.
*/
int motorHighLimitFromDriver_;
int motorLowLimitFromDriver_;
friend class pmacV3Axis;
}; };
#endif /* pmacV3Controller_H */ #endif /* pmacv3Controller_H */