21 Commits

Author SHA1 Message Date
354e9d90fb Fixed missing initializer for variables in Phytron-Axis 2025-06-20 13:46:10 +02:00
deea821e3f Merge pull request 'el734' (#2) from el734 into master
Reviewed-on: #2
2025-06-11 15:07:19 +02:00
7a46788fd5 Install a el734 db file
also remove explicit libversion in the makefile
2025-06-11 15:05:14 +02:00
9e77eb585c Merge branch 'lift_axis_no_autoenable' 2025-04-17 17:00:19 +02:00
d0c009ea38 Removed PMAC drivers
Commented out the PMAC drivers to avoid namespace clashes with the new
turboPmac driver library.
2025-04-17 16:52:52 +02:00
7e1fc78f76 Moved curses from top-level import to function-level import and added a
comment why that is necessary
2024-10-24 10:49:18 +02:00
9e0d8a4322 Added a new script utils/decodeMasterMACStatusR10.py which allows to
decode the R10 status message of the MasterMACs controller.

Also fixed a bug in utils/deltatau.py (error when printing too much text
at once)
2024-10-24 10:34:19 +02:00
3cccfe930c Removed typo from C804Axis.cpp 2024-10-18 09:53:47 +02:00
8860d0c59f Updated the first-time-poll of C804 Axis 2024-10-18 09:48:17 +02:00
b6c38be113 Initial driver version for the C804 controller 2024-10-18 09:48:17 +02:00
b14b50c25a Merge branch 'can-we-have-pipelines' into 'master'
Adds CI-Pipeline with Formatting, Linter Checks and Build Steps

See merge request sinqdev/sinqepicsapp!4
2024-10-14 10:07:16 +02:00
477ffdbc0b Adds CI-Pipeline with Formatting, Linter Checks and Build Steps 2024-10-14 10:07:16 +02:00
0a23ec8f22 clang is too old 2024-10-10 13:22:09 +02:00
eb1bb58c36 Fixed an uninitialized memory bug: In AmorDetectorAxis, the variables
det_starting and det_startTime were not initialized before reading them
in the poll function, leading to erratic behaviour.
2024-10-04 17:04:56 +02:00
80205727c7 File pmacAxis.h:
The default constructor of LiftAxis just forwards to the pmacAxis
constructor, which has an optional argument "autoenable" with the default
value "true". However, we want that argument to be false, hence we provide
an explicit constructor.

File C804Axis.cpp:
Removed a typing error

File Makefile.RHEL8:
Switched compilation target name to 2024-amor-no-autoenable-lift-axis to
not disturb other instruments. The newly created library is meant just
for Amor. if no problems occur, we can upstream the changes to master
at the end of October and create a new library "2023-v3".
2024-10-04 14:57:21 +02:00
39098fd0d1 Adds .clang-format style for formatting files 2024-09-25 16:21:01 +02:00
d44fdbf736 Updated the first-time-poll of C804 Axis 2024-09-23 16:21:02 +02:00
20e5c35d44 Initial driver version for the C804 controller 2024-09-23 16:21:02 +02:00
1539bfc66a bugfix: don't need to run on initialisation 2024-09-23 09:04:08 +02:00
d88e5877a7 Bugfix: A disabled PMac is no longer consider moving 2024-09-18 10:57:57 +02:00
118e177e04 Bugfix: A disabled PMac is no longer consider moving 2024-09-18 10:48:59 +02:00
15 changed files with 863 additions and 328 deletions

236
.clang-format Normal file
View File

@ -0,0 +1,236 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...

51
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,51 @@
default:
image: docker.psi.ch:5000/wall_e/sinqepics:latest
stages:
- test
- build
cppcheck:
stage: test
script:
- cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 sinqEPICSApp/
allow_failure: true # Long term this needs to be removed
artifacts:
expire_in: 1 week
tags:
- docker
formatting:
stage: test
script:
- clang-format --style=file --Werror --dry-run sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h
allow_failure: true # Long term this needs to be removed
artifacts:
expire_in: 1 week
tags:
- docker
# clangtidy:
# stage: test
# script:
# - curl https://docker.psi.ch:5000/v2/_catalog
# # - dnf update -y
# # - dnf install -y clang-tools-extra
# # - clang-tidy sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h -checks=cppcoreguidelines-*,cert-*
# # tags:
# # - docker
build_module:
stage: build
script:
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile.RHEL8
- make -f Makefile.RHEL8 install
- cp -rT "/ioc/modules/sinq/$(ls -U /ioc/modules/sinq/ | head -1)" "./sinq-${CI_COMMIT_SHORT_SHA}"
artifacts:
name: "sinq-${CI_COMMIT_SHORT_SHA}"
paths:
- "sinq-${CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week
when: always
tags:
- docker

View File

@ -4,21 +4,19 @@ include /ioc/tools/driver.makefile
MODULE=sinq MODULE=sinq
BUILDCLASSES=Linux BUILDCLASSES=Linux
EPICS_VERSIONS=7.0.7 EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL% ARCH_FILTER=RHEL8%
# additional module dependencies # additional module dependencies
REQUIRED+=SynApps REQUIRED+=SynApps
REQUIRED+=stream REQUIRED+=stream
REQUIRED+=scaler REQUIRED+=scaler
REQUIRED+=asynMotor REQUIRED+=motorBase
# Release version
LIBVERSION=2024-dev
# DB files to include in the release # DB files to include in the release
TEMPLATES += sinqEPICSApp/Db/dimetix.db TEMPLATES += sinqEPICSApp/Db/dimetix.db
TEMPLATES += sinqEPICSApp/Db/slsvme.db TEMPLATES += sinqEPICSApp/Db/slsvme.db
TEMPLATES += sinqEPICSApp/Db/spsamor.db TEMPLATES += sinqEPICSApp/Db/spsamor.db
TEMPLATES += sinqEPICSApp/Db/el734.db
# DBD files to include in the release # DBD files to include in the release
DBDS += sinqEPICSApp/src/sinq.dbd DBDS += sinqEPICSApp/src/sinq.dbd
@ -32,9 +30,9 @@ SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
SOURCES += sinqEPICSApp/src/stptok.cpp SOURCES += sinqEPICSApp/src/stptok.cpp
SOURCES += sinqEPICSApp/src/PhytronDriver.cpp SOURCES += sinqEPICSApp/src/PhytronDriver.cpp
SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp
SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c # SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c
SOURCES += sinqEPICSApp/src/pmacAxis.cpp # SOURCES += sinqEPICSApp/src/pmacAxis.cpp
SOURCES += sinqEPICSApp/src/pmacController.cpp # SOURCES += sinqEPICSApp/src/pmacController.cpp
SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp
SOURCES += sinqEPICSApp/src/C804Axis.cpp SOURCES += sinqEPICSApp/src/C804Axis.cpp
SOURCES += sinqEPICSApp/src/C804Controller.cpp SOURCES += sinqEPICSApp/src/C804Controller.cpp

View File

@ -38,3 +38,12 @@ Those political problems require a special development model:
Take care of the sinqEPICsApp/src/sinq.dbd file. This is the one which differs mostly between Take care of the sinqEPICsApp/src/sinq.dbd file. This is the one which differs mostly between
amorsim and master branches. amorsim and master branches.
# Formatting
Formatting is done via the [`.clang-format`](./.clang-format) file checked into
the repository. One option to apply the formatting to a given file is via the
command below.
```
clang-format -i -style=file <file>
```

34
sinqEPICSApp/Db/el734.db Normal file
View File

@ -0,0 +1,34 @@
record(motor,"$(P)$(M)")
{
field(DESC,"$(DESC)")
field(DTYP,"$(DTYP)")
field(DIR,"$(DIR)")
field(VELO,"$(VELO)")
field(HVEL,"$(VELO)")
field(VBAS,"$(VELO)")
field(VMAX, "${VMAX}")
field(ACCL,"$(ACCL)")
field(BDST,"$(BDST)")
field(BVEL,"$(BVEL)")
field(BACC,"$(BACC)")
field(OUT,"@asyn($(PORT),$(ADDR))")
field(MRES,"$(MRES)")
field(PREC,"$(PREC)")
field(EGU,"$(EGU)")
field(DHLM,"$(DHLM)")
field(DLLM,"$(DLLM)")
field(INIT,"$(INIT)")
field(PINI, "NO")
field(TWV,"1")
field(RTRY,"0")
}
# The message text
record(waveform, "$(P)$(M)-MsgTxt") {
field(DTYP, "asynOctetRead")
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
field(FTVL, "CHAR")
field(NELM, "80")
field(SCAN, "I/O Intr")
}

View File

@ -50,14 +50,5 @@ record(longin, "$(P)$(M):Enable_RBV") {
record(longout, "$(P)$(M):Reread_Encoder") { record(longout, "$(P)$(M):Reread_Encoder") {
field(DTYP, "asynInt32") field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(N),1) REREAD_ENCODER_POSITION") field(OUT, "@asyn($(PORT),$(N),1) REREAD_ENCODER_POSITION")
field(PINI, "YES")
}
# reread encoder
record(longin, "$(P)$(M):Reread_Encoder_RBV") {
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),$(N),1) REREAD_ENCODER_POSITION_RBV")
field(PINI, "NO") field(PINI, "NO")
field(SCAN, "1 second")
} }

View File

@ -1,14 +1,14 @@
#include "C804Axis.h" #include "C804Axis.h"
#include "C804Controller.h" #include "C804Controller.h"
#include <errlog.h>
#include <string.h>
#include <math.h>
#include <cmath> #include <cmath>
#include <unistd.h> #include <errlog.h>
#include <limits> #include <limits>
#include <math.h>
#include <string.h>
#include <unistd.h>
C804Axis::C804Axis(C804Controller *pC, int axisNo) : SINQAxis(pC, axisNo), pC_(pC) C804Axis::C804Axis(C804Controller *pC, int axisNo)
{ : SINQAxis(pC, axisNo), pC_(pC) {
/* /*
The superclass constructor SINQAxis calls in turn its superclass constructor The superclass constructor SINQAxis calls in turn its superclass constructor
asynMotorAxis. In the latter, a pointer to the constructed object this is asynMotorAxis. In the latter, a pointer to the constructed object this is
@ -16,13 +16,14 @@ C804Axis::C804Axis(C804Controller *pC, int axisNo) : SINQAxis(pC, axisNo), pC_(p
pC->pAxes_[axisNo] = this; pC->pAxes_[axisNo] = this;
Therefore, the axes are managed by the controller pC. See C804Controller.cpp for further explanation. Therefore, the axes are managed by the controller pC. See C804Controller.cpp
If axisNo is out of bounds, asynMotorAxis prints an error (see for further explanation. If axisNo is out of bounds, asynMotorAxis prints an
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorAxis.cpp, line 40). error (see
However, we want the IOC creation to stop completely, since this is a configuration error. https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorAxis.cpp,
line 40). However, we want the IOC creation to stop completely, since this
is a configuration error.
*/ */
if (axisNo >= pC->numAxes_) if (axisNo >= pC->numAxes_) {
{
exit(-1); exit(-1);
} }
last_position_steps_ = 0; last_position_steps_ = 0;
@ -30,8 +31,7 @@ C804Axis::C804Axis(C804Controller *pC, int axisNo) : SINQAxis(pC, axisNo), pC_(p
last_poll_ = 0.0; last_poll_ = 0.0;
} }
C804Axis::~C804Axis(void) C804Axis::~C804Axis(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.
} }
@ -39,47 +39,44 @@ C804Axis::~C804Axis(void)
/* /*
The polling function informs us about the state of the axis, in particular if it The polling function informs us about the state of the axis, in particular if it
is currently moving. It is called periodically, with the period defined by is currently moving. It is called periodically, with the period defined by
the controller constructor arguments idlePollPeriod and movingPollPeriod depending on the controller constructor arguments idlePollPeriod and movingPollPeriod
the current axis state. depending on the current axis state.
*/ */
asynStatus C804Axis::poll(bool *moving) asynStatus C804Axis::poll(bool *moving) {
{
// Local variable declaration // Local variable declaration
static const char *functionName = "C804Axis::poll"; static const char *functionName = "C804Axis::poll";
// The poll function is just a wrapper around poll_no_param_lib_update and handles mainly // The poll function is just a wrapper around poll_no_param_lib_update and
// the callParamCallbacks() function // handles mainly the callParamCallbacks() function
asynStatus status_poll = C804Axis::poll_no_param_lib_update(moving); asynStatus status_poll = C804Axis::poll_no_param_lib_update(moving);
// According to the function documentation of asynMotorAxis::poll, this // According to the function documentation of asynMotorAxis::poll, this
// function should be called at the end of a poll implementation. // function should be called at the end of a poll implementation.
asynStatus status_callback = callParamCallbacks(); asynStatus status_callback = callParamCallbacks();
if (status_callback != asynSuccess) if (status_callback != asynSuccess) {
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Updating the parameter library failed for axis %d\n", functionName, axisNo_); "%s: Updating the parameter library failed for axis %d\n",
functionName, axisNo_);
return status_callback; return status_callback;
} } else {
else
{
return status_poll; return status_poll;
} }
} }
// Perform the actual poll // Perform the actual poll
asynStatus C804Axis::poll_no_param_lib_update(bool *moving) asynStatus C804Axis::poll_no_param_lib_update(bool *moving) {
{
// Local variable declaration // Local variable declaration
static const char *functionName = "C804Axis::poll"; static const char *functionName = "C804Axis::poll";
asynStatus status; asynStatus status;
int axis_status = 0; int axis_status = 0;
// The controller returns the position and velocity in encoder steps. // The controller returns the position and velocity in encoder steps.
// This value needs to be converted in user units (engineering units EGU) via // This value needs to be converted in user units (engineering units EGU)
// the record field MRES of the motor record. This field has already been read // via the record field MRES of the motor record. This field has already
// by the constructor into the member variable motorRecResolution_. // been read by the constructor into the member variable
// To go from steps to user units, multiply with motorRecResolution_ // motorRecResolution_. To go from steps to user units, multiply with
// Example: If 10 steps correspond to 1 mm, MRES should be 0.1. // motorRecResolution_ Example: If 10 steps correspond to 1 mm, MRES should
// be 0.1.
int position_error_steps = 0; int position_error_steps = 0;
int motor_position_steps = 0; int motor_position_steps = 0;
int motor_velocity_steps = 0; int motor_velocity_steps = 0;
@ -89,28 +86,28 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
double motor_velocity = .0; double motor_velocity = .0;
double programmed_motor_velocity = .0; double programmed_motor_velocity = .0;
// The buffer sizes for command and response are defined in the controller (see the corresponding source code files) // The buffer sizes for command and response are defined in the controller
// (see the corresponding source code files)
char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_]; char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_];
/* /*
Cancel the poll if the last poll has "just" happened. Cancel the poll if the last poll has "just" happened.
*/ */
if (time(NULL) < last_poll_ + 0.5 * pC_->movingPollPeriod_) if (time(NULL) < last_poll_ + 0.5 * pC_->movingPollPeriod_) {
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING,
"%s: Aborted poll since the last poll for axis %d happened a short time ago\n", functionName, axisNo_); "%s: Aborted poll since the last poll for axis %d happened a "
"short time ago\n",
functionName, axisNo_);
return asynSuccess; return asynSuccess;
} } else {
else
{
last_poll_ = time(NULL); last_poll_ = time(NULL);
} }
/* /*
The parameter motorRecResolution_ is coupled to the field MRES of the motor The parameter motorRecResolution_ is coupled to the field MRES of the motor
record in the following manner: record in the following manner:
- In sinq_asyn_motor.db, the PV (motor_record_pv_name)MOTOR_REC_RESOLUTION is - In sinq_asyn_motor.db, the PV (motor_record_pv_name)MOTOR_REC_RESOLUTION
defined as a copy of the field (motor_record_pv_name).MRES: is defined as a copy of the field (motor_record_pv_name).MRES:
record(ao,"$(P)$(M):Resolution") { record(ao,"$(P)$(M):Resolution") {
field(DESC, "$(M) resolution") field(DESC, "$(M) resolution")
@ -121,51 +118,32 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
field(PREC, "$(PREC)") field(PREC, "$(PREC)")
} }
- The PV name MOTOR_REC_RESOLUTION is coupled in asynMotorController.h to the constant motorRecResolutionString - The PV name MOTOR_REC_RESOLUTION is coupled in asynMotorController.h to
- ... which in turn is assigned to motorRecResolution_ in asynMotorController.cpp the constant motorRecResolutionString
This way of making the field visible to the driver is described here: https://epics.anl.gov/tech-talk/2020/msg00378.php - ... which in turn is assigned to motorRecResolution_ in
This is a one-way coupling, changes to the parameter library via setDoubleParam asynMotorController.cpp This way of making the field visible to the driver
are NOT transferred to (motor_record_pv_name).MRES or to (motor_record_pv_name):Resolution. 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 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 save the read result to the member variable earlier), since the parameter
is updated at a later stage! library is updated at a later stage!
*/ */
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_, &motorRecResolution_); pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
&motorRecResolution_);
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, "Poll axis %d\n", axisNo_);
/* /*
The poll function might be called at IOC startup before the parameter We know that the motor resolution must not be zero. During the startup of
library has been fully initialized. In this case, calling getDoubleParam the IOC, polls can happen before the record is fully initialized. In that
returns the error status 10 (asynParamUndefined). Returning an asynError case, all values are zero.
from the poll method means that the poll is repeated. This is exactly what */
we want, because this means that the poll will be repeated until the if (motorRecResolution_ == 0) {
parameter library has been initialized.
asynStatus is defined as
typedef enum {
asynSuccess,asynTimeout,asynOverflow,asynError,asynDisconnected,asynDisabled
}asynStatus;
in asynDriver.h (see https://github.com/epics-modules/asyn/blob/master/asyn/asynDriver/asynDriver.h),
Therefore, it should only have the values 0 to 5. However, the enum value
range is extended in paramErrors.h (https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/paramErrors.h)
*/
if (status == asynParamUndefined)
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Parameter library is not yet initialized. Repeating poll on axis %d\n", functionName, axisNo_);
return asynError; return asynError;
} }
else if (status != asynSuccess)
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Reading the motor resolution failed for axis %d\n (asynStatus = %d)", functionName, axisNo_, status);
return asynError;
}
/* /*
Assume that the axis does not have a status problem. If it does have a Assume that the axis does not have a status problem. If it does have a
@ -176,11 +154,11 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
*/ */
setIntegerParam(pC_->motorStatusProblem_, false); setIntegerParam(pC_->motorStatusProblem_, false);
// Read out the position error of the axis (delta of target position to actual position) // Read out the position error of the axis (delta of target position to
// actual position)
snprintf(command, pC_->C804_MAXBUF_ - 1, "%dTE", axisNo_); snprintf(command, pC_->C804_MAXBUF_ - 1, "%dTE", axisNo_);
status = pC_->lowLevelWriteRead(axisNo_, command, response, true); status = pC_->lowLevelWriteRead(axisNo_, command, response, true);
if (status == asynSuccess) if (status == asynSuccess) {
{
int parsed_axis; int parsed_axis;
sscanf(response, "%2dE%10d", &parsed_axis, &position_error_steps); sscanf(response, "%2dE%10d", &parsed_axis, &position_error_steps);
@ -188,12 +166,12 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
position_error = double(position_error_steps) * motorRecResolution_; position_error = double(position_error_steps) * motorRecResolution_;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Axis %d, response %s, position error %f\n", functionName, axisNo_, response, position_error); "%s: Axis %d, response %s, position error %f\n", functionName,
} axisNo_, response, position_error);
else } else {
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Reading the position error failed for axis %d\n", functionName, axisNo_); "%s: Reading the position error failed for axis %d\n",
functionName, axisNo_);
setIntegerParam(pC_->motorStatusProblem_, true); setIntegerParam(pC_->motorStatusProblem_, true);
// Stop the evaluation prematurely // Stop the evaluation prematurely
@ -202,9 +180,9 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
// Read the current position. // Read the current position.
snprintf(command, this->pC_->C804_MAXBUF_ - 1, "%dTP", this->axisNo_); snprintf(command, this->pC_->C804_MAXBUF_ - 1, "%dTP", this->axisNo_);
status = this->pC_->lowLevelWriteRead(this->axisNo_, command, response, true); status =
if (status == asynSuccess) this->pC_->lowLevelWriteRead(this->axisNo_, command, response, true);
{ if (status == asynSuccess) {
int parsed_axis; int parsed_axis;
sscanf(response, "%2dP%10d", &parsed_axis, &motor_position_steps); sscanf(response, "%2dP%10d", &parsed_axis, &motor_position_steps);
@ -212,14 +190,14 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
motor_position = double(motor_position_steps) * motorRecResolution_; motor_position = double(motor_position_steps) * motorRecResolution_;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Axis %d, response %s, position %f\n", functionName, axisNo_, response, motor_position); "%s: Axis %d, response %s, position %f\n", functionName,
axisNo_, response, motor_position);
setDoubleParam(pC_->motorPosition_, motor_position); setDoubleParam(pC_->motorPosition_, motor_position);
setDoubleParam(pC_->motorEncoderPosition_, motor_position); setDoubleParam(pC_->motorEncoderPosition_, motor_position);
} } else {
else
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Reading the position failed for axis %d\n", functionName, axisNo_); "%s: Reading the position failed for axis %d\n", functionName,
axisNo_);
setIntegerParam(pC_->motorStatusProblem_, true); setIntegerParam(pC_->motorStatusProblem_, true);
return status; return status;
@ -227,9 +205,9 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
// Read the current velocity // Read the current velocity
snprintf(command, this->pC_->C804_MAXBUF_ - 1, "%dTV", this->axisNo_); snprintf(command, this->pC_->C804_MAXBUF_ - 1, "%dTV", this->axisNo_);
status = this->pC_->lowLevelWriteRead(this->axisNo_, command, response, true); status =
if (status == asynSuccess) this->pC_->lowLevelWriteRead(this->axisNo_, command, response, true);
{ if (status == asynSuccess) {
int parsed_axis; int parsed_axis;
sscanf(response, "%2dV%10d", &parsed_axis, &motor_velocity_steps); sscanf(response, "%2dV%10d", &parsed_axis, &motor_velocity_steps);
@ -237,12 +215,12 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
motor_velocity = double(motor_velocity_steps) * motorRecResolution_; motor_velocity = double(motor_velocity_steps) * motorRecResolution_;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Axis %d, response %s, velocity %f\n", functionName, axisNo_, response, motor_velocity); "%s: Axis %d, response %s, velocity %f\n", functionName,
} axisNo_, response, motor_velocity);
else } else {
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Reading the velocity failed for axis %d\n", functionName, axisNo_); "%s: Reading the velocity failed for axis %d\n", functionName,
axisNo_);
setIntegerParam(pC_->motorStatusProblem_, true); setIntegerParam(pC_->motorStatusProblem_, true);
return status; return status;
@ -250,23 +228,25 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
// Read the programmed velocity // Read the programmed velocity
snprintf(command, this->pC_->C804_MAXBUF_ - 1, "%dTY", this->axisNo_); snprintf(command, this->pC_->C804_MAXBUF_ - 1, "%dTY", this->axisNo_);
status = this->pC_->lowLevelWriteRead(this->axisNo_, command, response, true); status =
if (status == asynSuccess) this->pC_->lowLevelWriteRead(this->axisNo_, command, response, true);
{ if (status == asynSuccess) {
int parsed_axis; int parsed_axis;
sscanf(response, "%2dY%10d", &parsed_axis, &programmed_motor_velocity_steps); sscanf(response, "%2dY%10d", &parsed_axis,
&programmed_motor_velocity_steps);
// Scale from the encoder resultion to user units // Scale from the encoder resultion to user units
programmed_motor_velocity = double(programmed_motor_velocity_steps) * motorRecResolution_; programmed_motor_velocity =
double(programmed_motor_velocity_steps) * motorRecResolution_;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Axis %d, response %s, programmed velocity %f\n", functionName, axisNo_, response, programmed_motor_velocity); "%s: Axis %d, response %s, programmed velocity %f\n",
} functionName, axisNo_, response, programmed_motor_velocity);
else } else {
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Reading the programmed velocity failed for axis %d\n", functionName, axisNo_); "%s: Reading the programmed velocity failed for axis %d\n",
functionName, axisNo_);
setIntegerParam(pC_->motorStatusProblem_, true); setIntegerParam(pC_->motorStatusProblem_, true);
return status; return status;
@ -275,15 +255,13 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
// Read the motor status // Read the motor status
snprintf(command, pC_->C804_MAXBUF_ - 1, "%dTS", axisNo_); snprintf(command, pC_->C804_MAXBUF_ - 1, "%dTS", axisNo_);
status = pC_->lowLevelWriteRead(this->axisNo_, command, response, true); status = pC_->lowLevelWriteRead(this->axisNo_, command, response, true);
if (status == asynSuccess) if (status == asynSuccess) {
{
int parsed_axis; int parsed_axis;
sscanf(response, "%2dS%10d", &parsed_axis, &axis_status); sscanf(response, "%2dS%10d", &parsed_axis, &axis_status);
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"%s: Axis %d, response %s, status %d\n", functionName, axisNo_, response, axis_status); "%s: Axis %d, response %s, status %d\n", functionName,
} axisNo_, response, axis_status);
else } else {
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Reading the motor status %d\n", functionName, axisNo_); "%s: Reading the motor status %d\n", functionName, axisNo_);
setIntegerParam(pC_->motorStatusProblem_, true); setIntegerParam(pC_->motorStatusProblem_, true);
@ -292,24 +270,22 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
return status; return status;
} }
// Check if the axis is enabled by reading out bit 2 (see https://stackoverflow.com/questions/2249731/how-do-i-get-bit-by-bit-data-from-an-integer-value-in-c) // Check if the axis is enabled by reading out bit 2 (see
// https://stackoverflow.com/questions/2249731/how-do-i-get-bit-by-bit-data-from-an-integer-value-in-c)
int mask = 1 << 2; int mask = 1 << 2;
int masked_n = axis_status & mask; int masked_n = axis_status & mask;
// Is 1 if the axis is disabled // Is 1 if the axis is disabled
int disabled = masked_n >> 2; int disabled = masked_n >> 2;
if (disabled) if (disabled) {
{
enabled_ = false; enabled_ = false;
} } else {
else
{
enabled_ = true; enabled_ = true;
} }
/* /*
Determine if the motor is moving. This is determined by the following criteria: Determine if the motor is moving. This is determined by the following
1) The motor position changes from poll to poll criteria: 1) The motor position changes from poll to poll 2) The motor is
2) The motor is enabled enabled
*/ */
*moving = enabled_ && motor_position_steps != this->last_position_steps_; *moving = enabled_ && motor_position_steps != this->last_position_steps_;
@ -322,22 +298,21 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
moving. If it has spent too much time in a moving state without reaching moving. If it has spent too much time in a moving state without reaching
the target, stop the motor and return an error. the target, stop the motor and return an error.
*/ */
if (*moving) if (*moving) {
{
int motorStatusMoving = 0; int motorStatusMoving = 0;
pC_->getIntegerParam(axisNo_, pC_->motorStatusMoving_, &motorStatusMoving); pC_->getIntegerParam(axisNo_, pC_->motorStatusMoving_,
&motorStatusMoving);
// motor is moving, but didn't move in the last poll // motor is moving, but didn't move in the last poll
if (motorStatusMoving == 0) if (motorStatusMoving == 0) {
{
time_t current_time = time(NULL); time_t current_time = time(NULL);
// Factor 2 of the calculated moving time // Factor 2 of the calculated moving time
estimatedArrivalTime_ = current_time + std::ceil(2 * std::fabs(position_error) / programmed_motor_velocity); estimatedArrivalTime_ =
} current_time + std::ceil(2 * std::fabs(position_error) /
else programmed_motor_velocity);
{ } else {
// /* // /*
// Motor is moving for a longer time than it should: Stop it // Motor is moving for a longer time than it should: Stop it
// */ // */
@ -345,7 +320,9 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
// { // {
// snprintf(command, pC_->C804_MAXBUF_ - 1, "%dST", axisNo_); // snprintf(command, pC_->C804_MAXBUF_ - 1, "%dST", axisNo_);
// status = pC_->lowLevelWriteRead(axisNo_, command, response); // status = pC_->lowLevelWriteRead(axisNo_, command, response);
// asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Stopped axis %d since it moved for double the time it should to reach its target\n", functionName, axisNo_); // asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: Stopped
// axis %d since it moved for double the time it should to reach
// its target\n", functionName, axisNo_);
// } // }
} }
} }
@ -361,54 +338,58 @@ asynStatus C804Axis::poll_no_param_lib_update(bool *moving)
return status; return status;
} }
asynStatus C804Axis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration) asynStatus C804Axis::move(double position, int relative, double minVelocity,
{ double maxVelocity, double acceleration) {
asynStatus status; asynStatus status;
static const char *functionName = "C804Axis::move"; static const char *functionName = "C804Axis::move";
char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_]; char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_];
double position_c_units = 0.0; // Controller units double position_c_units = 0.0; // Controller units
int position_steps = 0; int position_steps = 0;
// Convert from user coordinates (EGU) to controller coordinates (steps). Check for overflow // Convert from user coordinates (EGU) to controller coordinates (steps).
if (motorRecResolution_ == 0.0) // Check for overflow
{ if (motorRecResolution_ == 0.0) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s: MRES must not be zero. Movement is aborted", functionName); asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: MRES must not be zero. Movement is aborted",
functionName);
return asynError; return asynError;
} }
position_c_units = position / motorRecResolution_; position_c_units = position / motorRecResolution_;
// Check for overflow during the division // Check for overflow during the division
if (position_c_units * motorRecResolution_ != position) if (position_c_units * motorRecResolution_ != position) {
{ asynPrint(
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: could not convert from user units (%f) to controller units (user units divided by resolution MRES %f) due to overflow.", "%s: could not convert from user units (%f) to controller units "
functionName, position, motorRecResolution_); "(user units divided by resolution MRES %f) due to overflow.",
functionName, position, motorRecResolution_);
return asynError; return asynError;
} }
// Steps can only be integer values => cast to integer while checking for overflow // Steps can only be integer values => cast to integer while checking for
if (std::numeric_limits<int>::max() < position_c_units || std::numeric_limits<int>::min() > position_c_units) // overflow
{ if (std::numeric_limits<int>::max() < position_c_units ||
std::numeric_limits<int>::min() > position_c_units) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: target position %f cannot be converted to int (overflow). Check target value %f and MRES %f", "%s: target position %f cannot be converted to int "
functionName, position_c_units, position_c_units, motorRecResolution_); "(overflow). Check target value %f and MRES %f",
functionName, position_c_units, position_c_units,
motorRecResolution_);
return asynError; return asynError;
} }
position_steps = static_cast<int>(position_c_units); position_steps = static_cast<int>(position_c_units);
// Convert from relative to absolute values // Convert from relative to absolute values
if (relative) if (relative) {
{
position_steps += last_position_steps_; position_steps += last_position_steps_;
} }
// If the axis is currently disabled, enable it // If the axis is currently disabled, enable it
if (!enabled_) if (!enabled_) {
{
snprintf(command, pC_->C804_MAXBUF_ - 1, "%dGO", axisNo_); snprintf(command, pC_->C804_MAXBUF_ - 1, "%dGO", axisNo_);
status = pC_->lowLevelWriteRead(this->axisNo_, command, response, false); status =
if (status != asynSuccess) pC_->lowLevelWriteRead(this->axisNo_, command, response, false);
{ if (status != asynSuccess) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Enabling axis %d\n failed", functionName, axisNo_); "%s: Enabling axis %d\n failed", functionName, axisNo_);
return status; return status;
@ -418,10 +399,10 @@ asynStatus C804Axis::move(double position, int relative, double minVelocity, dou
// Start movement // Start movement
snprintf(command, pC_->C804_MAXBUF_ - 1, "%dMA%d", axisNo_, position_steps); snprintf(command, pC_->C804_MAXBUF_ - 1, "%dMA%d", axisNo_, position_steps);
status = pC_->lowLevelWriteRead(this->axisNo_, command, response, false); status = pC_->lowLevelWriteRead(this->axisNo_, command, response, false);
if (status != asynSuccess) if (status != asynSuccess) {
{
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s: Setting the target position %d failed for axis %d\n", functionName, position_steps, axisNo_); "%s: Setting the target position %d failed for axis %d\n",
functionName, position_steps, axisNo_);
setIntegerParam(pC_->motorStatusProblem_, true); setIntegerParam(pC_->motorStatusProblem_, true);
return status; return status;
} }
@ -433,14 +414,13 @@ asynStatus C804Axis::move(double position, int relative, double minVelocity, dou
return status; return status;
} }
asynStatus C804Axis::moveVelocity(double min_velocity, double max_velocity, double acceleration) asynStatus C804Axis::moveVelocity(double min_velocity, double max_velocity,
{ double acceleration) {
static const char *functionName = "C804Axis::moveVelocity"; static const char *functionName = "C804Axis::moveVelocity";
return asynError; return asynError;
} }
asynStatus C804Axis::stop(double acceleration) asynStatus C804Axis::stop(double acceleration) {
{
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
static const char *functionName = "C804Axis::stop"; static const char *functionName = "C804Axis::stop";
char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_]; char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_];
@ -448,26 +428,28 @@ asynStatus C804Axis::stop(double acceleration)
bool moving = false; bool moving = false;
poll(&moving); poll(&moving);
if (moving) if (moving) {
{
// ST = Stop // ST = Stop
snprintf(command, pC_->C804_MAXBUF_ - 1, "%dST", axisNo_); snprintf(command, pC_->C804_MAXBUF_ - 1, "%dST", axisNo_);
status = pC_->lowLevelWriteRead(axisNo_, command, response, false); status = pC_->lowLevelWriteRead(axisNo_, command, response, false);
asynPrint(pC_->pasynUserSelf, ASYN_TRACEIO_DEVICE, "%s: Stop axis %d\n", functionName, axisNo_); asynPrint(pC_->pasynUserSelf, ASYN_TRACEIO_DEVICE, "%s: Stop axis %d\n",
functionName, axisNo_);
} }
return status; return status;
} }
asynStatus C804Axis::home(double minVelocity, double maxVelocity, double acceleration, int forwards) asynStatus C804Axis::home(double minVelocity, double maxVelocity,
{ double acceleration, int forwards) {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
static const char *functionName = "C804Axis::home"; static const char *functionName = "C804Axis::home";
char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_]; char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_];
snprintf(command, pC_->C804_MAXBUF_ - 1, "%dFE0", axisNo_); // Home to the upper limit of the axis (25 mm) snprintf(command, pC_->C804_MAXBUF_ - 1, "%dFE0",
axisNo_); // Home to the upper limit of the axis (25 mm)
status = pC_->lowLevelWriteRead(axisNo_, command, response, false); status = pC_->lowLevelWriteRead(axisNo_, command, response, false);
asynPrint(pC_->pasynUserSelf, ASYN_TRACEIO_DEVICE, "%s: Homing axis %d\n", functionName, axisNo_); asynPrint(pC_->pasynUserSelf, ASYN_TRACEIO_DEVICE, "%s: Homing axis %d\n",
functionName, axisNo_);
return status; return status;
} }
@ -475,23 +457,21 @@ asynStatus C804Axis::home(double minVelocity, double maxVelocity, double acceler
/** /**
If on is 0, disable the motor, otherwise enable it. If on is 0, disable the motor, otherwise enable it.
*/ */
asynStatus C804Axis::enable(int on) asynStatus C804Axis::enable(int on) {
{
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
static const char *functionName = "C804Axis::enable"; static const char *functionName = "C804Axis::enable";
char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_]; char command[pC_->C804_MAXBUF_], response[pC_->C804_MAXBUF_];
if (on == 0) if (on == 0) {
{
snprintf(command, pC_->C804_MAXBUF_ - 1, "%dMF", axisNo_); snprintf(command, pC_->C804_MAXBUF_ - 1, "%dMF", axisNo_);
status = pC_->lowLevelWriteRead(axisNo_, command, response, false); status = pC_->lowLevelWriteRead(axisNo_, command, response, false);
asynPrint(pC_->pasynUserSelf, ASYN_TRACEIO_DEVICE, "%s: Disable axis %d\n", functionName, axisNo_); asynPrint(pC_->pasynUserSelf, ASYN_TRACEIO_DEVICE,
} "%s: Disable axis %d\n", functionName, axisNo_);
else } else {
{
snprintf(command, pC_->C804_MAXBUF_ - 1, "%dMN", axisNo_); snprintf(command, pC_->C804_MAXBUF_ - 1, "%dMN", axisNo_);
status = pC_->lowLevelWriteRead(axisNo_, command, response, false); status = pC_->lowLevelWriteRead(axisNo_, command, response, false);
asynPrint(pC_->pasynUserSelf, ASYN_TRACEIO_DEVICE, "%s: Enable axis %d\n", functionName, axisNo_); asynPrint(pC_->pasynUserSelf, ASYN_TRACEIO_DEVICE,
"%s: Enable axis %d\n", functionName, axisNo_);
} }
return status; return status;
} }

View File

@ -143,6 +143,11 @@ asynStatus EL734Controller::transactController(int axisNo, char command[COMLEN],
pasynOctetSyncIO->flush(pasynUserController_); pasynOctetSyncIO->flush(pasynUserController_);
if (axis != NULL)
{
axis->updateMsgTxtFromDriver("");
}
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command), status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
reply, COMLEN, 2., &out, &in, &reason); reply, COMLEN, 2., &out, &in, &reason);
if (status != asynSuccess) if (status != asynSuccess)

View File

@ -250,6 +250,8 @@ PhytronAxis::PhytronAxis(PhytronController *pC, int axisNo, int enc)
haveBrake = 0; haveBrake = 0;
brakeIO = -1; brakeIO = -1;
next_poll = -1; next_poll = -1;
homing = 0;
homing_direction = 0;
} }
int PhytronAxis::setBrake(int brakeNO) int PhytronAxis::setBrake(int brakeNO)

View File

@ -114,7 +114,7 @@ typedef struct {
unsigned int dbInit; unsigned int dbInit;
}EL737priv; }EL737priv;
static void dummyAsynCallback([[maybe_unused]] asynUser *pasynUser) static void dummyAsynCallback(asynUser *pasynUser)
{ {
} }

View File

@ -1186,6 +1186,8 @@ AmorDetectorAxis::AmorDetectorAxis(pmacController *pC, int axisNo, int function)
pC_->debugFlow(functionName); pC_->debugFlow(functionName);
_function = function; _function = function;
det_starting = false;
det_startTime = time(NULL);
callParamCallbacks(); callParamCallbacks();

View File

@ -18,86 +18,91 @@
#ifndef pmacAxis_H #ifndef pmacAxis_H
#define pmacAxis_H #define pmacAxis_H
#include "SINQController.h"
#include "SINQAxis.h" #include "SINQAxis.h"
#include "SINQController.h"
class pmacController; class pmacController;
class SeleneController; class SeleneController;
class pmacAxis : public SINQAxis class pmacAxis : public SINQAxis {
{
public: public:
/* These are the methods we override from the base class */ /* These are the methods we override from the base class */
pmacAxis(pmacController *pController, int axisNo, bool enable=true); pmacAxis(pmacController *pController, int axisNo, bool enable = true);
virtual ~pmacAxis(); virtual ~pmacAxis();
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration); asynStatus move(double position, int relative, double min_velocity,
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration); double max_velocity, double acceleration);
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards); asynStatus moveVelocity(double min_velocity, double max_velocity,
asynStatus stop(double acceleration); double acceleration);
asynStatus poll(bool *moving); asynStatus home(double min_velocity, double max_velocity,
asynStatus setPosition(double position); double acceleration, int forwards);
asynStatus enable(int on); asynStatus stop(double acceleration);
asynStatus poll(bool *moving);
asynStatus setPosition(double position);
asynStatus enable(int on);
protected: protected:
pmacController *pC_; pmacController *pC_;
asynStatus getAxisStatus(bool *moving); asynStatus getAxisStatus(bool *moving);
asynStatus getAxisInitialStatus(void); asynStatus getAxisInitialStatus(void);
double setpointPosition_; double setpointPosition_;
double encoderPosition_; double encoderPosition_;
double currentVelocity_; double currentVelocity_;
double velocity_; double velocity_;
double accel_; double accel_;
double highLimit_; double highLimit_;
double lowLimit_; double lowLimit_;
double scale_; double scale_;
double previous_position_; double previous_position_;
int previous_direction_; int previous_direction_;
int encoder_axis_; int encoder_axis_;
int axisErrorCount; int axisErrorCount;
time_t startTime; time_t startTime;
time_t status6Time; time_t status6Time;
int starting; int starting;
int homing; int homing;
double statusPos; double statusPos;
time_t next_poll; time_t next_poll;
bool autoEnable; bool autoEnable;
friend class pmacController; friend class pmacController;
friend class pmacV3Controller; friend class pmacV3Controller;
}; };
/*--------------------------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------------------------*/
class SeleneAxis : public pmacAxis class SeleneAxis : public pmacAxis {
{
public: public:
SeleneAxis(SeleneController *pController, int axisNo, double limitTarget); SeleneAxis(SeleneController *pController, int axisNo, double limitTarget);
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration); asynStatus move(double position, int relative, double min_velocity,
asynStatus setPosition(double position); double max_velocity, double acceleration);
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards); asynStatus setPosition(double position);
asynStatus home(double min_velocity, double max_velocity,
double acceleration, int forwards);
protected: protected:
friend class SeleneController; friend class SeleneController;
friend class pmacController; friend class pmacController;
private:
double limitTarget;
asynStatus getSeleneAxisInitialStatus(void);
private:
double limitTarget;
asynStatus getSeleneAxisInitialStatus(void);
}; };
/* /*
Yet another special set of motors for the Selene Guide at AMOR. Each segment can be lifted or tilted. This is Yet another special set of motors for the Selene Guide at AMOR. Each segment
two motors. One acts as a slave and only writes a new target, the other also sets a new target and sends the can be lifted or tilted. This is two motors. One acts as a slave and only
actual movement command. Both motors are coordianted in the motor controller in order to avoid tension on writes a new target, the other also sets a new target and sends the actual
the guide elements. This gaves rise to the function code LIFTSLAVE and LIFTMASTER. movement command. Both motors are coordianted in the motor controller in order
to avoid tension on the guide elements. This gaves rise to the function code
LIFTSLAVE and LIFTMASTER.
In another mode the whole guide can be lifted or tilted. Then motor 1 and 6 get new values and one of them In another mode the whole guide can be lifted or tilted. Then motor 1 and 6
sends the drive command. This causes all 6 motors to drive synchronously to their new targets. This is get new values and one of them sends the drive command. This causes all 6
implemented through the LIFTSEGMENT function code. motors to drive synchronously to their new targets. This is implemented
through the LIFTSEGMENT function code.
Mark Koennecke, February 2020 Mark Koennecke, February 2020
@ -106,57 +111,63 @@ class SeleneAxis : public pmacAxis
Michele Brambilla, February 2020 Michele Brambilla, February 2020
*/ */
class LiftAxis : public pmacAxis class LiftAxis : public pmacAxis {
{ public:
public: /*
LiftAxis(pmacController *pController, int axisNo) : pmacAxis((pmacController *)pController,axisNo) {}; The default constructor of LiftAxis just forwards to the pmacAxis
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration); constructor, which has an optional argument "autoenable" with the default
asynStatus poll(bool *moving); value "true". However, we want that argument to be false, hence we provide
asynStatus stop(double acceleration); an explicit constructor.
private: */
int waitStart; LiftAxis(pmacController *pController, int axisNo)
: pmacAxis((pmacController *)pController, axisNo, false) {};
asynStatus move(double position, int relative, double min_velocity,
double max_velocity, double acceleration);
asynStatus poll(bool *moving);
asynStatus stop(double acceleration);
private:
int waitStart;
}; };
/******************************************** /********************************************
* Protocol version 3 requires just some minor change * Protocol version 3 requires just some minor change
* *
* Michele Brambilla, February 2022 * Michele Brambilla, February 2022
********************************************/ ********************************************/
class pmacV3Axis : public pmacAxis { class pmacV3Axis : public pmacAxis {
public: public:
pmacV3Axis(pmacController *pController, int axisNo);
pmacV3Axis(pmacController *pController, int axisNo); asynStatus move(double position, int relative, double min_velocity,
double max_velocity, double acceleration);
asynStatus poll(bool *moving);
asynStatus move(double position, int relative, double min_velocity, protected:
double max_velocity, double acceleration); int IsEnable;
asynStatus poll(bool *moving); double Speed;
protected:
int IsEnable;
double Speed;
asynStatus getAxisStatus(bool *moving); asynStatus getAxisStatus(bool *moving);
friend class pmacController;
friend class pmacV3Controller;
};
/*----------------------------------------------------------------------------------------------*/
class pmacHRPTAxis : public pmacV3Axis
{
public:
pmacHRPTAxis(pmacController *pController, int axisNo) : pmacV3Axis(pController,axisNo) {};
/**
* Override getAxisStatus in order to read the special parameter indicating a
* slit blade crash at HRPT
*/
asynStatus getAxisStatus(bool *moving);
protected:
friend class pmacController;
friend class pmacController;
friend class pmacV3Controller;
}; };
/*----------------------------------------------------------------------------------------------*/
class pmacHRPTAxis : public pmacV3Axis {
public:
pmacHRPTAxis(pmacController *pController, int axisNo)
: pmacV3Axis(pController, axisNo) {};
/**
* Override getAxisStatus in order to read the special parameter indicating
* a slit blade crash at HRPT
*/
asynStatus getAxisStatus(bool *moving);
protected:
friend class pmacController;
};
/* /*
* Special motors for the AMOR detector movement. The whole * Special motors for the AMOR detector movement. The whole
@ -171,39 +182,41 @@ class pmacHRPTAxis : public pmacV3Axis
#define ADCOZ 2 #define ADCOZ 2
#define ADPARK 3 #define ADPARK 3
class AmorDetectorAxis: public pmacAxis { class AmorDetectorAxis : public pmacAxis {
public: public:
AmorDetectorAxis(pmacController *pController, int axisNo, int function); AmorDetectorAxis(pmacController *pController, int axisNo, int function);
asynStatus move(double position, int relative, double min_velocity, asynStatus move(double position, int relative, double min_velocity,
double max_velocity, double acceleration); double max_velocity, double acceleration);
asynStatus poll(bool *moving); asynStatus poll(bool *moving);
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration); asynStatus moveVelocity(double min_velocity, double max_velocity,
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards); double acceleration);
asynStatus stop(double acceleration); asynStatus home(double min_velocity, double max_velocity,
asynStatus setPosition(double position); double acceleration, int forwards);
asynStatus stop(double acceleration);
asynStatus setPosition(double position);
protected: protected:
int _function; int _function;
int det_starting; int det_starting;
time_t det_startTime; time_t det_startTime;
}; };
/*----------------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------------*/
class GirderAxis : public pmacV3Axis {
class GirderAxis: public pmacV3Axis {
public: public:
GirderAxis(pmacController *pController, int axisNo); GirderAxis(pmacController *pController, int axisNo);
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration); asynStatus move(double position, int relative, double min_velocity,
double max_velocity, double acceleration);
asynStatus stop(double acceleration); asynStatus stop(double acceleration);
asynStatus poll(bool *moving); asynStatus poll(bool *moving);
protected: protected:
int IsEnable; int IsEnable;
friend class pmacController; friend class pmacController;
friend class pmacV3Controller; friend class pmacV3Controller;
}; };
#endif /* pmacAxis_H */ #endif /* pmacAxis_H */

View File

@ -5,9 +5,9 @@ registrar(EL734Register)
registrar(PhytronRegister) registrar(PhytronRegister)
registrar(EuroMoveRegister) registrar(EuroMoveRegister)
registrar(NanotecRegister) registrar(NanotecRegister)
registrar(pmacControllerRegister) # registrar(pmacControllerRegister)
registrar(C804ControllerRegister) registrar(C804ControllerRegister)
registrar(pmacAsynIPPortRegister) # registrar(pmacAsynIPPortRegister)
registrar(MasterMACSRegister) registrar(MasterMACSRegister)
registrar(SINQControllerRegister) registrar(SINQControllerRegister)

213
utils/decodeMasterMACStatusR10.py Executable file
View File

@ -0,0 +1,213 @@
#!/usr/bin/env python3
# List of tuples which encodes the states given in the file description.
# Index first with the bit index, then with the bit value
interpretation = [
("Not ready to be switched on", "Ready to be switched on"), # Bit 0
("Not switched on", "Switched on"), # Bit 1
("Disabled", "Enabled"), # Bit 2
("Ok", "Fault condition set"), # Bit 3
("Motor supply voltage absent ", "Motor supply voltage present"), # Bit 4
("Motor performs quick stop", "Ok"), # Bit 5
("Switch on enabled", "Switch on disabled"), # Bit 6
("Ok", "RWarning: Movement function was called while motor is still moving. The function call is ignored"), # Bit 7
("Motor is idle", "Motor is currently moving"), # Bit 8
("Motor does not execute command messages (local mode)", "Motor does execute command messages (remote mode)"), # Bit 9
("Target not reached", "Target reached"), # Bit 10
("Ok", "Internal limit active"), # Bit 11
("Not specified", "Not specified"), # Bit 12
("Not specified", "Not specified"), # Bit 13
("No event set or event has not occurred yet", "Set event has occurred"), # Bit 14
("Axis off (power disabled)", "Axis on (power enabled)"), # Bit 15
]
def decode(value, big_endian: bool = False):
interpreted = []
bit_list = [(value >> shift_ind) & 1
for shift_ind in range(value.bit_length())] # little endian
if big_endian:
bit_list.reverse() # big endian
for (bit, interpretations) in zip(bit_list, interpretation):
interpreted.append(interpretations[bit])
return (bit_list, interpreted)
def print_decoded(bit_list, interpreted):
for (idx, (bit_value, msg)) in enumerate(zip(bit_list, interpreted)):
print(f"Bit {idx} = {bit_value}: {msg}")
def interactive():
# Imported here, because curses is not available in Windows. Using the
# interactive mode therefore fails on Windows, but at least the single
# command mode can be used (which would not be possible if we would import
# curses at the top level)
import curses
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
stdscr.keypad(True)
stdscr.scrollok(True)
stdscr.addstr(">> ")
stdscr.refresh()
history = [""]
ptr = len(history) - 1
while True:
c = stdscr.getch()
if c == curses.KEY_RIGHT:
(y, x) = stdscr.getyx()
if x < len(history[ptr]) + 3:
stdscr.move(y, x+1)
stdscr.refresh()
elif c == curses.KEY_LEFT:
(y, x) = stdscr.getyx()
if x > 3:
stdscr.move(y, x-1)
stdscr.refresh()
elif c == curses.KEY_UP:
if ptr > 0:
ptr -= 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_DOWN:
if ptr < len(history) - 1:
ptr += 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_ENTER or c == ord('\n') or c == ord('\r'):
if history[ptr] == 'quit':
break
# because of arrow keys move back to the end of the line
(y, x) = stdscr.getyx()
stdscr.move(y, 3+len(history[ptr]))
if history[ptr]:
result = interpret_inputs(history[ptr].split())
if result is None:
stdscr.addstr(f"\nBAD INPUT: Expected input of 'value [big_endian]', where 'value' is an int or a float and 'big_endian' is an optional boolean argument.")
else:
(arg, big_endian) = result
(bit_list, interpreted) = decode(arg, big_endian)
for (idx, (bit_value, msg)) in enumerate(zip(bit_list, interpreted)):
stdscr.addstr(f"\nBit {idx} = {bit_value}: {msg}")
stdscr.refresh()
if ptr == len(history) - 1 and history[ptr] != "":
history += [""]
else:
history[-1] = ""
ptr = len(history) - 1
stdscr.addstr("\n>> ")
stdscr.refresh()
else:
if ptr < len(history) - 1: # Modifying previous input
if len(history[-1]) == 0:
history[-1] = history[ptr]
ptr = len(history) - 1
else:
history += [history[ptr]]
ptr = len(history) - 1
if c == curses.KEY_BACKSPACE:
if len(history[ptr]) == 0:
continue
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-4] + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x-1)
stdscr.refresh()
else:
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x+1)
stdscr.refresh()
# to quit
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
curses.endwin()
def interpret_inputs(inputs):
number = None
big_endian = False
try:
number = int(float(inputs[0]))
if len(inputs) > 1:
second_arg = inputs[1]
if second_arg == "True" or second_arg == "true":
big_endian = True
elif second_arg == "False" or second_arg == "false":
big_endian = False
else:
big_endian = bool(int(second_arg))
return (number, big_endian)
except:
return None
if __name__ == "__main__":
from sys import argv
if len(argv) == 1:
# Start interactive mode
interactive()
else:
result = interpret_inputs(argv[1:])
if result is None:
print("""
Decode R10 message of MasterMACs
------------------
MasterMACs returns its status message (R10) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeMasterMACStatusR10.py value [big_endian]
'value' is the return value of a R10 command. This value is interpreted
bit-wise and the result is printed out. The optional second argument can
be used to specify whether the input value needs to be interpreted as
little or big endian. Default is False.
Option 2: CLI Mode
------------------
Usage: decodeMasterMACStatusR10.py
A prompt will be opened. Type in the return value of a R10 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
""")
else:
print("Motor status")
print("============")
(arg, big_endian) = result
(bit_list, interpreted) = decode(arg, big_endian)
print_decoded(bit_list, interpreted)

View File

@ -64,6 +64,7 @@ if __name__ == "__main__":
curses.noecho() curses.noecho()
curses.cbreak() curses.cbreak()
stdscr.keypad(True) stdscr.keypad(True)
stdscr.scrollok(True)
stdscr.addstr(">> ") stdscr.addstr(">> ")
stdscr.refresh() stdscr.refresh()