Compare commits

...

20 Commits

Author SHA1 Message Date
cff64f5ecf Added new feature to set deadband
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 12s
The field SPDB can now be populated via either the substitutions file or
from inside the driver (using the motorPositionDeadband paramLib entry).
2025-09-09 16:50:26 +02:00
7a0de4e9d9 Perform callParamCallbacks even if movement watchdog timed out
All checks were successful
Test And Build / Lint (push) Successful in 6s
Test And Build / Build (push) Successful in 5s
2025-08-14 17:15:21 +02:00
566728c57c Fixed error in README.md
All checks were successful
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Successful in 5s
2025-08-12 09:15:55 +02:00
8689c79f19 Adjusted formatting
All checks were successful
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Successful in 6s
2025-08-12 09:10:00 +02:00
7ed054d075 Fixed formatting
All checks were successful
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Successful in 6s
2025-08-12 09:09:03 +02:00
7965dd3b2e Refactored the HIDDEN macro into its own header
All checks were successful
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Successful in 5s
2025-08-12 09:07:32 +02:00
c19e4845e4 Hide visibility of sinqController classes
All checks were successful
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Successful in 6s
2025-08-12 08:51:54 +02:00
4d27783062 Fixed some further errors in the docs
All checks were successful
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Successful in 6s
2025-07-24 12:04:47 +02:00
5273feef6c Fixed wrong description of IOC startup script
All checks were successful
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Successful in 6s
2025-07-24 12:03:28 +02:00
cccfc79860 Added documentation regarding virtual methods
All checks were successful
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Successful in 6s
2025-07-24 11:54:10 +02:00
c65a8de5dd Made forcedPoll non-virtual so it doesn't break the ABI
All checks were successful
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Successful in 6s
2025-07-24 11:29:15 +02:00
1910eda0b1 Added forcedPoll method which skips the adaptive polling checks
All checks were successful
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Successful in 6s
The poll method does various tasks. One of them is skipping the poll if
adaptive polling is enabled and certain conditions are fulfilled (see
docs). However, sometimes it is needed to force a poll regardless.
Therefore, a new method forcedPoll was added which is wrapped by poll
now and does all tasks of the previous poll function except for the skip
check.
2025-07-24 11:13:37 +02:00
977016bdb4 Reverted commit 4a0c09bd7f (this should have been done in a branch)
All checks were successful
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Successful in 6s
2025-07-23 17:07:54 +02:00
ed77125378 Merge branch 'main' of gitea.psi.ch:lin-epics-modules/sinqMotor
All checks were successful
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Successful in 6s
2025-07-23 09:53:58 +02:00
4a0c09bd7f Push DLLM should process at each poll, even if the value stays the same 2025-07-23 09:53:49 +02:00
1fe21ec192 adds gitea action
All checks were successful
Test And Build / Lint (push) Successful in 4s
Test And Build / Build (push) Successful in 5s
2025-07-04 14:16:14 +02:00
2fd4851313 Expandend upon dependency usage in README.md 2025-06-26 14:02:59 +02:00
55a9fe6f3e Fixed another calling signature bug 2025-06-18 08:25:39 +02:00
e618b39687 Fixed function call arguments in template 2025-06-18 08:15:34 +02:00
41dfd1de5a Fixed docs 2025-06-17 13:13:43 +02:00
10 changed files with 202 additions and 94 deletions

View File

@@ -0,0 +1,23 @@
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
make install

View File

@@ -1,41 +0,0 @@
default:
image: docker.psi.ch:5000/sinqdev/sinqepics:latest
stages:
- lint
- build
- test
cppcheck:
stage: lint
script:
- cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
artifacts:
expire_in: 1 week
tags:
- sinq
formatting:
stage: lint
script:
- clang-format --style=file --Werror --dry-run src/*.cpp
artifacts:
expire_in: 1 week
tags:
- sinq
build_module:
stage: build
script:
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
- make install
- cp -rT "/ioc/modules/sinqMotor/$(ls -U /ioc/modules/sinqMotor/ | head -1)" "./sinqMotor-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
artifacts:
name: "sinqMotor-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
paths:
- "sinqMotor-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week
when: always
tags:
- sinq

View File

@@ -62,30 +62,28 @@ To find out which version of sinqMotor is needed by a driver, refer to its Makef
### IOC startup script
An EPICS IOC for motor control at SINQ is started by executing a script with the IOC shell. In its simplest form, an IOC for two controllers is a file looking like this:
An EPICS IOC for motor control at SINQ is started by executing a script with the IOC shell. In its simplest form, an IOC for two controllers run by "exampleDriver" is a file looking like this:
```
#!/usr/local/bin/iocsh
# Load libraries needed for the IOC
require sinqMotor, 1.0.0
require actualDriver, 1.2.0
require exampleDriver, 1.0.0
# Define environment variables used later to parametrize the individual controllers
epicsEnvSet("TOP","/ioc/sinq-ioc/sinqtest-ioc/")
epicsEnvSet("INSTR","SQ:SINQTEST:")
# Include other scripts for the controllers 1 and 2
< actualDriver.cmd
< actualDriver.cmd
< exampleDriver1.cmd
< exampleDriver2.cmd
iocInit()
```
The first line is a so-called shebang which instructs Linux to execute the file with the executable located at the given path - the IOC shell in this case. The controller script "mcu1.cmd" looks like this:
The script for controller 1 ("turboPmac1.cmd") for a Turbo PMAC (see https://git.psi.ch/sinq-epics-modules/turboPmac) has the following structure. The scripts for other controller types can be found in the README.md of their respective repositories.
The first line is a so-called shebang which instructs Linux to execute the file with the executable located at the given path - the IOC shell in this case. The files `exampleDriver1.cmd` or `exampleDriver2` then look like this:
```
# Define the name of the controller and the corresponding port
epicsEnvSet("DRIVER_PORT","actualDriver1")
epicsEnvSet("DRIVER_PORT","exampleDriver1")
epicsEnvSet("IP_PORT","p$(DRIVER_PORT)")
# Create the TCP/IP socket used to talk with the controller. The socket can be adressed from within the IOC shell via the port name
@@ -116,17 +114,17 @@ setForcedFastPolls("$(DRIVER_PORT)", 10);
setThresholdComTimeout("$(DRIVER_PORT)", 300, 10);
# Parametrize the EPICS record database with the substitution file named after the motor controller.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
epicsEnvSet("SINQDBPATH","$(exampleDriver_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(DRIVER_PORT).substitutions", "INSTR=$(INSTR)$(DRIVER_PORT):,CONTROLLER=$(DRIVER_PORT)")
epicsEnvSet("SINQDBPATH","$(actualDriver_DB)/turboPmac.db")
epicsEnvSet("SINQDBPATH","$(exampleDriver_DB)/turboPmac.db")
dbLoadTemplate("$(TOP)/$(DRIVER_PORT).substitutions", "INSTR=$(INSTR)$(DRIVER_PORT):,CONTROLLER=$(DRIVER_PORT)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(IP_PORT)")
dbLoadRecords("$(exampleDriver_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(IP_PORT)")
```
### Substitution file
The substitution file is a table containing axis-specific information which is used to create the axis-specific PVs.
To work with sinqMotor, "mcu1.substitutions" needs to look like this (the order of columns does not matter):
To work with sinqMotor, `exampleDriver1.substitutions` needs to look like this (the order of columns does not matter):
```
file "$(SINQDBPATH)"
{
@@ -206,9 +204,11 @@ transferred to (motor_record_pv_name).MRES or to
## Developer guide
### Base classes
### File structure
sinqMotor offers a variety of additional methods for children classes to standardize certain patterns (e.g. writing messages to the IOC shell and the motor message PV). For a detailed description, please see the respective function documentation in the .h-files. All of these functions can be overwritten manually if e.g. a completely different implementation of `poll` is required. Some functions are marked as virtual, because they are called from other functions of sinqMotor and therefore need runtime polymorphism. Functions without that marker are not called anywhere in sinqMotor.
sinqMotor offers a variety of additional methods for children classes to standardize certain patterns (e.g. writing messages to the IOC shell and the motor message PV). For a detailed description, please see the respective function documentation in the .h-files. All of these functions can be overwritten manually if e.g. a completely different implementation of `poll` is required. Some functions are marked as virtual, because they are called from other functions of sinqMotor and therefore need runtime polymorphism. Functions without that marker are not called anywhere in sinqMotor (except for `forcedPoll`, which is called in `poll`).
Adding new virtual methods breaks the ABI and therefore warrants a new major version number!
#### sinqController.h
- `couldNotParseResponse`: Write a standardized message if parsing a device response failed.
@@ -230,7 +230,15 @@ It calls `doReset` and performs some fast polls after `doReset` returns.
- `doMove`: This is an empty function which should be overwritten by concrete driver implementations.
- `home`: This function sets the internal status flags for the homing process and then calls doHome.
- `doHome`: This is an empty function which should be overwritten by concrete driver implementations.
- `poll`: This is a wrapper around `doPoll` which performs some bookkeeping tasks before and after calling `doPoll`:
- `poll`: This is a wrapper around `forcedPoll` which does the following checks before calling `forcedPoll`:
- Are there any outstanding fast polls (method `outstandingForcedFastPolls` of the controller returns a value greater zero)?
- Was the axis moving last time its status was polled?
- Is adaptive polling disabled?
- Did an idle period pass since the last poll?
If all of these conditions are false, no poll is performed. Otherwise, the `forcedPoll` method is called.
This method should not be called in the driver code itself if a poll is needed - use `forcedPoll` instead!
- `forcedPoll`: This is a wrapper around `doPoll` which performs some bookkeeping tasks before and after calling `doPoll`:
Before calling `doPoll`:
- Check if the paramLib already contains an old error message. If so, put it into a temporary bufffer
@@ -249,12 +257,27 @@ It calls `doReset` and performs some fast polls after `doReset` returns.
- `startMovTimeoutWatchdog`: Starts a watchdog for the movement time. This watchdog compares the actual time spent in a movement operation with an expected time, which is calculated based on the distance of the current and the target position.
- `checkMovTimeoutWatchdog`: Check if the watchdog timed out.
- `setWatchdogEnabled`: Enables / disables the watchdog. This function is also available in the IOC shell.
- `setOffsetMovTimeout`: Set a linear offset for the expected movement time. This function is also available in the IOC shell.
- `setOffsetMovTimeout`: Set a constant offset for the expected movement time. This function is also available in the IOC shell.
- `setScaleMovTimeout`: Set a scaling factor for the expected movement time. This function is also available in the IOC shell.
#### msgPrintControl.h
In addition to the two extension classes this library also includes a mechanism which prevents excessive repetitions of the same error message to the IOC shell via the classes `msgPrintControl` and `msgPrintControlKey`. A detailed description of the mechanism can be found in the docstring of `msgPrintControl`. The implementation of the `poll` function of `sinqAxis` also contains an example how to use it. Using this feature in derived drivers is entirely optional.
#### macros.h
Contains macros used in `sinqMotor` and derived drivers:
- **HIDDEN**
By default, the symbols of classes and functions are hidden to avoid symbol clashes when loading
multiple shared libraries which use `sinqMotor`. In order to compile this library with exported
symbols, specifiy `-DHIDDEN= ` as a compiler flag (if using the given Makefile, this
needs to be added to the `USR_CFLAGS`).
Derived libraries can use the same mechanism via the macro `HIDDEN` (defined in `macros.h`):
```
class HIDDEN turboPmacController : public sinqController
```
### Versioning
The versioning is done via git tags. Git tags are recognized by the PSI build system: If you tag a version as 1.0, it will be built into the directory /ioc/modules/sinqMotor/1.0. The tag is directly coupled to a commit so that it is always clear which source code was used to build which binary.
@@ -277,18 +300,28 @@ To use the library when writing a concrete motor driver, include it in the makef
### Usage as static dependency
This repository is included as a git submodule in some of the driver repositories depending upon sinqMotor. When installing via a Makefile (`make install`) using the PSI build system, the following git command is executed within `/ioc/tools/driver.makefile`:
This repository is included as a git submodule in the driver repositories depending upon sinqMotor. When installing via a Makefile (`make install`) using the PSI build system, the following git command is executed within `/ioc/tools/driver.makefile`:
`git submodule update --init --recursive`
This forces each submodule to be checked out at the latest commit hash stored in the remote repository. However, this is usually unwanted behaviour, since the higher-level drivers are usually designed to be compiled against a specific version of sinqMotor. In order to set the submodule to a specific version, the following steps need to be done BEFORE calling `make install`:
- `cd sinqMotor`
- `git checkout 0.1`
- `git checkout 1.0`
- `cd ..`
Then, the fixation of the version to 0.1 needs to be committed in the parent repository:
Then, the fixation of the version to 1.0 needs to be committed in the parent repository:
- `git commit -m "Update sinqMotor to 0.1"`
- `git commit -m "Update sinqMotor to 1.0"`
After this commit, running `make install` will use the correct driver version for compilation.
After this commit, running `make install` will use the correct driver version for compilation.
If your driver uses another driver as a static dependency via git submodule which in turn includes a sinqMotor submodule, it is not necessary to specify the version of sinqMotor. Instead, specify the desired commit of the direct dependency, commit this change and then update all submodules:
- `cd turboPmac`
- `git checkout 1.0`
- `cd ..`
- `git commit -m "Update turboPmac to 1.0"`
- `git submodule update --init --recursive`
This will update sinqMotor to the version specified in the 1.0 commit of turboPmac.

View File

@@ -41,6 +41,7 @@ record(motor,"$(INSTR)$(M)")
field(PINI,"NO")
field(DHLM, "$(DHLM=0)")
field(DLLM, "$(DLLM=0)")
field(SPDB, "$(SPDB=0)")
field(TWV,"1")
field(RTRY,"0")
field(RDBD, "$(RDBD=10e300)") # Suppress retries and overshoot stop commands
@@ -238,6 +239,27 @@ record(ao, "$(INSTR)$(M):PushDLLM2Field") {
field(PINI, "NO")
}
# This record pair reads the parameter library value for "motorPositionDeadband"
# and pushes it to the motor record field "SPDP". This can be used to the position
# deadband from the hardware
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
# This record is coupled to the parameter library via motorPositionDeadband -> MOTOR_POSITION_DEADBAND.
record(ai, "$(INSTR)$(M):SPDB_RBV")
{
field(DTYP, "asynFloat64")
field(VAL, "$(SPDP=0)")
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_POSITION_DEADBAND")
field(SCAN, "I/O Intr")
field(FLNK, "$(INSTR)$(M):PushSPDB2Field")
field(PINI, "NO")
}
record(ao, "$(INSTR)$(M):PushSPDB2Field") {
field(DOL, "$(INSTR)$(M):SPDB_RBV NPP")
field(OUT, "$(INSTR)$(M).SPDB")
field(OMSL, "closed_loop")
field(PINI, "NO")
}
# This record pair reads the parameter library value for "motorVeloFromDriver_"
# and pushes it to the motor record field "VELO". This can be used to read the speed value
# from the hardware and correspondingly update the motor record from the driver.

22
src/macros.h Normal file
View File

@@ -0,0 +1,22 @@
// Collection of macros used in sinqMotor and derived classes
#ifndef macros_H
#define macros_H
/*
The macro "HIDDEN" hides the symbol of the annotated class / function. This is
useful to avoid symbol clashes when loading multiple shared libraries in a
single IOC. To override the hiding, add `-DHIDDEN= ` to your compiler
flags (in this case, the symbols will be exported with their default
visibility).
*/
#ifndef HIDDEN
#if defined(_WIN32) || defined(__CYGWIN__)
#define HIDDEN
#else
#define HIDDEN __attribute__((visibility("hidden")))
#endif
#endif
// macros_H
#endif

View File

@@ -6,6 +6,7 @@
#define DefaultMaxRepetitions 4
#include <asynDriver.h>
#include <macros.h>
#include <string.h>
#include <string>
#include <unordered_map>
@@ -15,7 +16,7 @@
* `msgPrintControl` on how to use this key.
*
*/
class msgPrintControlKey {
class HIDDEN msgPrintControlKey {
public:
std::string controller_;
@@ -82,7 +83,7 @@ template <> struct hash<msgPrintControlKey> {
* }
* ```
*/
class msgPrintControl {
class HIDDEN msgPrintControl {
public:
/**
* @brief Destroy the msgPrintControl object

View File

@@ -171,14 +171,7 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
sinqAxis::~sinqAxis() = default;
asynStatus sinqAxis::poll(bool *moving) {
// Local variable declaration
asynStatus pl_status = asynSuccess;
asynStatus poll_status = asynSuccess;
int homing = 0;
int adaptivePolling = 0;
char waitingMessage[pC_->MAXBUF_] = {0};
char newMessage[pC_->MAXBUF_] = {0};
/*
If adaptive polling is enabled:
@@ -201,22 +194,33 @@ asynStatus sinqAxis::poll(bool *moving) {
Check if both adaptive polling is enabled and no forced fast polls are still
required.
*/
if (adaptivePolling != 0 && pC_->outstandingForcedFastPolls() == 0) {
// Motor wasn't moving during the last poll
if (!pSinqA_->wasMoving) {
if (adaptivePolling != 0 && pC_->outstandingForcedFastPolls() == 0 &&
!pSinqA_->wasMoving) {
// Add the idle poll period
epicsTimeStamp earliestTimeNextPoll = pSinqA_->lastPollTime;
epicsTimeAddSeconds(&earliestTimeNextPoll, pC_->idlePollPeriod());
// Add the idle poll period
epicsTimeStamp earliestTimeNextPoll = pSinqA_->lastPollTime;
epicsTimeAddSeconds(&earliestTimeNextPoll, pC_->idlePollPeriod());
if (epicsTimeLessThanEqual(&earliestTimeNextPoll, &ts) == 0) {
*moving = false;
return asynSuccess;
}
if (epicsTimeLessThanEqual(&earliestTimeNextPoll, &ts) == 0) {
*moving = false;
return asynSuccess;
}
}
return forcedPoll(moving);
}
asynStatus sinqAxis::forcedPoll(bool *moving) {
// Local variable declaration
asynStatus pl_status = asynSuccess;
asynStatus poll_status = asynSuccess;
int homing = 0;
char waitingMessage[pC_->MAXBUF_] = {0};
char newMessage[pC_->MAXBUF_] = {0};
// Update the start time of the last poll
epicsTimeStamp ts;
epicsTimeGetCurrent(&ts);
pSinqA_->lastPollTime = ts;
/*
@@ -279,9 +283,12 @@ asynStatus sinqAxis::poll(bool *moving) {
pSinqA_->wasMoving = *moving;
}
// Check and update the watchdog
if (checkMovTimeoutWatchdog(*moving) != asynSuccess) {
return asynError;
// Check and update the watchdog as well as the general poll status IF the
// poll did not fail already.
if (poll_status == asynSuccess) {
if (checkMovTimeoutWatchdog(*moving) != asynSuccess) {
poll_status = asynError;
}
}
// According to the function documentation of asynMotorAxis::poll, this

View File

@@ -9,12 +9,13 @@ Stefan Mathis, November 2024
#ifndef sinqAxis_H
#define sinqAxis_H
#include "asynMotorAxis.h"
#include <macros.h>
#include <memory>
#include <type_traits>
struct sinqAxisImpl;
class epicsShareClass sinqAxis : public asynMotorAxis {
class HIDDEN sinqAxis : public asynMotorAxis {
public:
/**
* @brief Construct a new sinqAxis object
@@ -31,6 +32,27 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
*/
~sinqAxis();
/**
* @brief Check if a poll should be performed. If yes, call `forcedPoll`.
*
This is a wrapper around `forcedPoll` which does the following checks before
calling `forcedPoll`:
- Are there any outstanding fast polls (method `outstandingForcedFastPolls`
of the controller returns a value greater zero)?
- Was the axis moving last time its status was polled?
- Is adaptive polling disabled?
- Did an idle period pass since the last poll?
If all of these conditions are false, no poll is performed. Otherwise, the
`forcedPoll` method is called. This method should not be called in the
driver code itself if a poll is needed - use `forcedPoll` instead!
*
* @param moving Forwarded to `forcedPoll` or set to false
(depending on whether `forcedPoll was called`).
* @return asynStatus Forward the status of `forcedPoll` or set to
asynSuccess (depending on whether `forcedPoll was called`).
*/
virtual asynStatus poll(bool *moving);
/**
* @brief Perform some standardized operations before and after the concrete
`doPoll` implementation.
@@ -46,7 +68,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
- The flags `motorStatusHome_`, `motorStatusHomed_` and
`motorStatusAtHome_` are set to their idle values (0, 1 and 1 respectively)
in the `poll()` method once the homing procedure is finished. See the
in the `forcedPoll()` method once the homing procedure is finished. See the
documentation of the `home()` method for more details.
- Run `callParamCallbacks()`
@@ -56,9 +78,9 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
* @param moving Forwarded to `doPoll`.
* @return asynStatus Forward the status of `doPoll`, unless one of
the parameter library operation fails (in that case, returns the status of
the failed operation.
the failed operation).
*/
virtual asynStatus poll(bool *moving);
asynStatus forcedPoll(bool *moving);
/**
* @brief Implementation of the "proper", device-specific poll method. This
@@ -142,7 +164,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
*
* The flags `motorStatusHome_`, `motorStatusHomed_` and
`motorStatusAtHome_` are set to their idle values (0, 1 and 1 respectively)
in the `poll()` method once the homing procedure is finished.
in the `forcedPoll())` method once the homing procedure is finished.
*
* @param minVelocity Forwarded to `doHome`.
* @param maxVelocity Forwarded to `doHome`.
@@ -505,6 +527,8 @@ asynStatus setAxisParamImpl(A *axis, C *controller, const char *indexName,
* This function should not be used directly, but rather through its macro
* variant `setAxisParamChecked`.
*
* @tparam A
* @tparam C
* @tparam T
* @param axis
* @param controller
@@ -599,9 +623,9 @@ asynStatus getAxisParamImpl(A *axis, C *controller, const char *indexName,
const char *callerFunctionName, int lineNumber,
size_t msgSize, TypeTag<bool>) {
int readValueInt = 0;
asynStatus status =
getAxisParamImpl(axis, indexName, func, &readValueInt,
callerFunctionName, lineNumber, msgSize);
asynStatus status = getAxisParamImpl(axis, controller, indexName, func,
&readValueInt, callerFunctionName,
lineNumber, msgSize, TypeTag<int>{});
*readValue = readValueInt != 0;
return status;
}

View File

@@ -92,6 +92,7 @@ struct sinqControllerImpl {
int motorAcclFromDriver;
int motorHighLimitFromDriver;
int motorLowLimitFromDriver;
int motorPositionDeadband;
int adaptivePolling;
int encoderType;
};
@@ -267,6 +268,17 @@ sinqController::sinqController(const char *portName,
exit(-1);
}
status = createParam("MOTOR_POSITION_DEADBAND", asynParamFloat64,
&pSinqC_->motorPositionDeadband);
if (status != asynSuccess) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
"parameter failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
exit(-1);
}
status = createParam("MOTOR_ENABLE_MOV_WATCHDOG", asynParamInt32,
&pSinqC_->motorEnableMovWatchdog);
if (status != asynSuccess) {
@@ -699,6 +711,9 @@ int sinqController::motorHighLimitFromDriver() {
int sinqController::motorLowLimitFromDriver() {
return pSinqC_->motorLowLimitFromDriver;
}
int sinqController::motorPositionDeadband() {
return pSinqC_->motorPositionDeadband;
}
int sinqController::adaptivePolling() { return pSinqC_->adaptivePolling; }
int sinqController::encoderType() { return pSinqC_->encoderType; }

View File

@@ -12,6 +12,7 @@ Stefan Mathis, November 2024
#include "asynMotorController.h"
#include "msgPrintControl.h"
#include <initHooks.h>
#include <macros.h>
#include <memory>
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
@@ -20,9 +21,9 @@ Stefan Mathis, November 2024
#define AbsoluteEncoder "absolute"
#define NoEncoder "none"
struct sinqControllerImpl;
struct HIDDEN sinqControllerImpl;
class epicsShareClass sinqController : public asynMotorController {
class HIDDEN sinqController : public asynMotorController {
public:
/**
* @brief Construct a new sinqController object
@@ -314,6 +315,7 @@ class epicsShareClass sinqController : public asynMotorController {
int motorAcclFromDriver();
int motorHighLimitFromDriver();
int motorLowLimitFromDriver();
int motorPositionDeadband();
int adaptivePolling();
int encoderType();