Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
94edef6cd8 | |||
afdd66a648 | |||
74dedd9cd3 | |||
4765b59ee5 | |||
16ee667963 | |||
8bfaf7eb6f | |||
fd93c36639 | |||
926df04caf | |||
d575d4fe76 | |||
203bb9475f | |||
60960dde44 | |||
d837a8545b |
246
.clang-format
Normal file
246
.clang-format
Normal file
@ -0,0 +1,246 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: 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
|
||||
AllowBreakBeforeNoexceptSpecifier: Never
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortCompoundRequirementOnASingleLine: true
|
||||
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
|
||||
BreakAdjacentStringLiterals: true
|
||||
BreakAfterAttributes: Leave
|
||||
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
|
||||
PenaltyBreakScopeResolution: 500
|
||||
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
|
||||
SkipMacroDefinitionBody: false
|
||||
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
|
||||
AfterPlacementOperator: true
|
||||
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: 8
|
||||
UseTab: Never
|
||||
VerilogBreakBetweenInstancePorts: true
|
||||
WhitespaceSensitiveMacros:
|
||||
- BOOST_PP_STRINGIZE
|
||||
- CF_SWIFT_NAME
|
||||
- NS_SWIFT_NAME
|
||||
- PP_STRINGIZE
|
||||
- STRINGIZE
|
||||
...
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "turboPmac"]
|
||||
path = turboPmac
|
||||
url = https://gitea.psi.ch/lin-epics-modules/turboPmac
|
27
Makefile
27
Makefile
@ -9,26 +9,37 @@ ARCH_FILTER=RHEL%
|
||||
# Additional module dependencies
|
||||
REQUIRED+=motorBase
|
||||
|
||||
# Specify the version of motorBase we want to build against
|
||||
# Specify the version of motorBase we want to dynamically link against
|
||||
motorBase_VERSION=7.2.2
|
||||
|
||||
# Specify the version of turboPmac we want to build against
|
||||
turboPmac_VERSION=0.10.0
|
||||
|
||||
# These headers allow to depend on this library for derived drivers.
|
||||
HEADERS += src/auxiliaryAxis.h
|
||||
HEADERS += src/detectorTowerAxis.h
|
||||
HEADERS += src/detectorTowerAngleAxis.h
|
||||
HEADERS += src/detectorTowerController.h
|
||||
HEADERS += src/detectorTowerLiftAxis.h
|
||||
HEADERS += src/detectorTowerSupportAxis.h
|
||||
|
||||
# Source files to build
|
||||
SOURCES += src/auxiliaryAxis.cpp
|
||||
SOURCES += src/detectorTowerAxis.cpp
|
||||
SOURCES += turboPmac/sinqMotor/src/msgPrintControl.cpp
|
||||
SOURCES += turboPmac/sinqMotor/src/sinqAxis.cpp
|
||||
SOURCES += turboPmac/sinqMotor/src/sinqController.cpp
|
||||
SOURCES += turboPmac/src/pmacAsynIPPort.c
|
||||
SOURCES += turboPmac/src/turboPmacAxis.cpp
|
||||
SOURCES += turboPmac/src/turboPmacController.cpp
|
||||
SOURCES += turboPmac/src/pmacAsynIPPort.c
|
||||
SOURCES += src/detectorTowerAngleAxis.cpp
|
||||
SOURCES += src/detectorTowerController.cpp
|
||||
SOURCES += src/detectorTowerLiftAxis.cpp
|
||||
SOURCES += src/detectorTowerSupportAxis.cpp
|
||||
|
||||
# Store the record files
|
||||
TEMPLATES += turboPmac/sinqMotor/db/asynRecord.db
|
||||
TEMPLATES += turboPmac/sinqMotor/db/sinqMotor.db
|
||||
TEMPLATES += turboPmac/db/turboPmac.db
|
||||
TEMPLATES += db/detectorTower.db
|
||||
|
||||
# This file registers the motor-specific functions in the IOC shell.
|
||||
DBDS += turboPmac/sinqMotor/src/sinqMotor.dbd
|
||||
DBDS += turboPmac/src/turboPmac.dbd
|
||||
DBDS += src/detectorTower.dbd
|
||||
|
||||
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings
|
||||
|
75
README.md
75
README.md
@ -4,31 +4,50 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This is a driver for the detector tower which is based on the Turbo PMAC driver (https://git.psi.ch/sinq-epics-modules/turboPmac). It consists of the following three objects:
|
||||
- `detectorTowerAxis`: This is a virtual axis which controls multiple physical motors in order to provide a synchronized movement.
|
||||
- `detectorTowerController`: This is an expanded variant of `turboPmacController` provided by the Turbo PMAC library linked above.It is needed to operate a `detectorTowerAxis`, but it can also be used to operate a "normal" `turboPmacAxis`.
|
||||
- `auxiliaryAxis`: This is an auxiliary axis type attached to the main detector axis. Multiple instances of these axes are constructed automatically when creating a `detectorTowerAxis`.
|
||||
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 detector tower which is based on the Turbo PMAC driver (https://gitea.psi.ch/lin-epics-modules/turboPmac). It consists of the following four objects:
|
||||
- `detectorTowerController`: This is an expanded variant of `turboPmacController` provided by the Turbo PMAC library linked above.It is needed to operate a `detectorTowerAngleAxis`, but it can also be used to operate a "normal" `turboPmacAxis`.
|
||||
- `detectorTowerAngleAxis`: This is a virtual axis which controls multiple physical motors ($x$ and $z$) in order to provide a combined movement. Moving it results in a rotation of the entire beam around the support axis position ($\alpha$).
|
||||
- `detectorTowerLiftAxis`: This is a virtual axis which controls multiple physical motors in order to provide a combined movement. Moving it results in a vertical lift ($z_{lift}$).
|
||||
- `detectorTowerSupportAxis`: This is an axis at the rotation center of the beam which is part of the combined movements. Its origin can be shifted manually for small adjustments, resulting in a corresponding movement. Other than that, it is not meant to move on its own, hence setting a new value to the `VAL` field of the motor record won't cause it to move.
|
||||
|
||||
## User guide
|
||||
|
||||
The centerpiece of this driver is the `detectorTowerAxis`, which controls the angle of the detector flight tube. Creating an instance of this axis type also creates multiple so-called `auxiliaryAxis`, which are used to perform secondary movements.
|
||||
The centerpiece of this driver is the `detectorTowerAngleAxis`, which controls the angle of the detector flight tube. It is supported by two secondary axes, `detectorTowerLiftAxis` and `detectorTowerSupportAxis`. All three axes are created by a single IOC shell command `detectorTowerAxis` (see [Usage in IOC shell](#usage-in-ioc-shell)). All three axes use absolute encoders and therefore cannot perform a reference / home drive.
|
||||
|
||||
The utilities provided in the `utils` folder of https://git.psi.ch/sinq-epics-modules/turboPmac work with this driver as well.
|
||||
The first two axes can be moved independently from each other or together as a combined movement by issuing both move orders within a certain time span. This time span can be configured via the IOC shell function `setDeferredMovementWait` and defaults to 100 ms. This allows the user to start a combined movement e.g. via caput:
|
||||
|
||||
```
|
||||
caput $(ControllerPV):angle 2 & $(ControllerPV):caput lift 10
|
||||
```
|
||||
which moves the angle to 2 degree and shifts the entire beam vertically by 10 mm. When using the axis from NICOS, using the `maw` or `move` command with multiple devices has the same effect:
|
||||
|
||||
```
|
||||
move("angle", 2, "lift", 10)
|
||||
```
|
||||
|
||||
If one axis is already moving, no new move command can be issued until the movement is finished. The `detectorTowerSupportAxis` cannot be moved directly.
|
||||
|
||||
It is possible to shift the origin of all three axes (and therefore also moving the `detectorTowerSupportAxis`) for small adjustments. The current origin position in the axis unit (degree for `detectorTowerAngleAxis`, mm for `detectorTowerLiftAxis` and encoder steps for `detectorTowerSupportAxis`) can be read from the PV `$(INSTR)$(M):Origin`. The origin can be shifted by the amount of axis units written to `$(INSTR)$(M):AdjustOrigin`. Since this is a relative movement, writing "10" two times to `$(INSTR)$(M):AdjustOrigin` shifts the origin by 20 axis units. Therefore, the limits are only there to prevent too large shifts during a single write (since the origin can be shifted to any position by writing to `$(INSTR)$(M):AdjustOrigin` repeatedly).
|
||||
|
||||
The detector tower can be moved into a so-called "changer position" by writing "1" to the PV `$(INSTR)$(M):ChangeState`. This causes the entire tower to move vertically down. When it is in the changer position, the axes cannot be moved at all. In order to go back to the "working state" where the axes can be moved again, write "0" to `$(INSTR)$(M):ChangeState`. The current state of the axis can be read out from `$(INSTR)$(M):ChangingStateRBV`. In case starting a change movement succeeds, `$(INSTR)$(M):ChangeStateRBV` changes its value accordingly, otherwise it stays at its current value.
|
||||
|
||||
The utilities provided in the `utils` folder in `turboPmac/utils` work with this driver as well.
|
||||
|
||||
### Usage in IOC shell
|
||||
|
||||
detectorTower exports the following IOC shell functions:
|
||||
- `detectorTowerController`: Create a new controller object.
|
||||
- `detectorTowerAxis`: Create a new axis object.
|
||||
- `detectorTowerAxis`: Create a `detectorTowerAngleAxis`, a `detectorTowerLiftAxis` and a `detectorTowerSupportAxis` object and link them to each other
|
||||
|
||||
The constructor function for `detectorTowerAxis` has the following syntax:
|
||||
|
||||
```
|
||||
detectorTowerAxis("$(NAME)",1, 2, 3)
|
||||
detectorTowerAngleAxis("$(NAME)",1, 2, 3)
|
||||
```
|
||||
|
||||
with 1 being the axis number / index of the detector flight tube angle axis, 2 being the lift offset axis and 3 being the tilt offset axis. These axes are parametrized in the same way as any "normal" axes via a substitution file (see corresponding section below).
|
||||
with 1 being the axis number / index of the `detectorTowerAngleAxis`, 2 being the `detectorTowerLiftAxis` and 3 being the `detectorTowerSupportAxis`. These axes are parametrized in the same way as any "normal" axes via a substitution file (see corresponding section below).
|
||||
|
||||
"Normal" `turboPmacAxis` may be used together with this controller:
|
||||
|
||||
@ -48,8 +67,8 @@ drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
|
||||
# 1: Socket communication timeout in seconds
|
||||
detectorTowerController("$(NAME)", "$(ASYN_PORT)", 8, 0.05, 1, 1);
|
||||
|
||||
# Slot 1, 2 and 3 are occupied by a detector tower axis and its attached auxiliary axes, while the slots 4 and 5 are "normal" Turbo PMAC axes.
|
||||
detectorTowerAxis("$(NAME)",1, 2, 3);
|
||||
# Slot 1, 2 and 3 are occupied by the three detector tower axis, while the slots 4 and 5 are "normal" Turbo PMAC axes.
|
||||
detectorTowerAngleAxis("$(NAME)",1, 2, 3);
|
||||
turboPmacAxis("$(NAME)",4);
|
||||
turboPmacAxis("$(NAME)",5);
|
||||
|
||||
@ -61,9 +80,9 @@ setThresholdComTimeout("$(NAME)", 100, 1);
|
||||
|
||||
# Parametrize the EPICS record database with the substitution file named after the MCU.
|
||||
# Since this driver is based on Turbo PMAC, we need to parametrize turboPmac.db in addition to sinqMotor.db and detectorTower.db.
|
||||
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
|
||||
epicsEnvSet("SINQDBPATH","$(detectorTower_DB)/sinqMotor.db")
|
||||
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
||||
epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/turboPmac.db")
|
||||
epicsEnvSet("SINQDBPATH","$(detectorTower_DB)/turboPmac.db")
|
||||
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
||||
epicsEnvSet("SINQDBPATH","$(detectorTower_DB)/detectorTower.db")
|
||||
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
|
||||
@ -74,7 +93,7 @@ dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_POR
|
||||
|
||||
From the perspective of EPICS, the main detector flight tube axis and the auxiliary axes are independent axes and therefore each axis needs its own parametrization in the substitution file. If additional axes are used in the axis, they are parametrized as usual:
|
||||
```
|
||||
detectorTowerAxis("$(NAME)",1, 2, 3);
|
||||
detectorTowerAngleAxis("$(NAME)",1, 2, 3);
|
||||
turboPmacAxis("$(NAME)",4);
|
||||
turboPmacAxis("$(NAME)",5);
|
||||
```
|
||||
@ -83,9 +102,9 @@ file "$(SINQDBPATH)"
|
||||
{
|
||||
pattern
|
||||
{ AXIS, M, DESC, EGU, DIR, MRES, MSGTEXTSIZE, ENABLEMOVWATCHDOG, LIMITSOFFSET, CANSETSPEED }
|
||||
{ 1, "tower", "Angle of the beam guide", degree, Pos, 0.001, 200, 0, 1.0, 0 }
|
||||
{ 2, "liftZeroCorr", "Detector vertical lift offset", mm, Pos, 0.001, 200, 0, 1.0, 0 }
|
||||
{ 3, "tiltZeroCorr", "Detector tilt offset", degree, Pos, 0.001, 200, 0, 1.0, 0 }
|
||||
{ 1, "angle", "Angle of the beam guide", degree, Pos, 0.001, 200, 0, 1.0, 0 }
|
||||
{ 2, "lift", "Detector vertical lift offset", mm, Pos, 0.001, 200, 0, 1.0, 0 }
|
||||
{ 3, "suppoer", "Support axis", mm, Pos, 0.001, 200, 0, 1.0, 0 }
|
||||
{ 4, "other axis A", "A normal axis", degree, Pos, 0.001, 200, 1, 2.0, 1 }
|
||||
{ 5, "other axis B", "Another normal axis", degree, Pos, 0.001, 200, 1, 2.0, 0 }
|
||||
}
|
||||
@ -97,23 +116,11 @@ Note that the speed of the detector tower axes 1, 2 and 3 cannot be set. Setting
|
||||
|
||||
### Code architecture
|
||||
|
||||
The code is designed around the `detectorTowerAxis`, which controls the angle of the beam guide and acts as a "master" axis. Other movements are realized as auxiliary axes (e.g. for the vertical lift offset and the tilt offset). The `detectorTowerAxis` has pointers to all associated auxiliary axes and (as described above) its IOC shell constructor also builds the associated auxiliary axes.
|
||||
The code is designed around the `detectorTowerAngleAxis`, which controls the angle of the beam guide and acts as a "master" axis which contains pointers to its attached `detectorTowerLiftAxis` and `detectorTowerSupportAxis`. All three axes are polled at once via the function `detectorTowerController::pollDetectorAxes`, which is called from the individual `poll` functions of the axes (the `doPoll` mechanism from `sinqMotor` is not used). To avoid polling the axes multiple times during one controller cycle, the function `detectorTowerController::pollDetectorAxes` is only executed for the axis with the smallest index. Since this axis is polled first, the other two axes are therefore already up-to-date when they execute their own poll function. If one of the axes is moving, all three axes are marked as moving. The same is true for errors.
|
||||
|
||||
The `doPoll` implementation for `detectorTowerAxis` queries the status of both the `detectorTowerAxis` itself and all associated auxiliary axes. If any of the axes is moving, the `detectorTowerAxis` is set to "moving" as well. In turn, the `doPoll` implementation of a `auxiliaryAxis` checks if its associated `detectorTowerAxis` is moving and sets its own movement status accordingly.
|
||||
In order to save on movement time, movement commands to auxiliary axes and the `detectorTowerAngleAxis` are collected and then send as a single resulting movement command to the MCU. In order to do so, a "collector" thread is running which checks if a movement request has been send to one of the axes. If that is the case, it waits for `detectorTowerAngleAxis->deferredMovementWait_` seconds and checks if commands for other axes are given as well. Then, it calls `detectorTowerAngleAxis::startCombinedMove` which combines all commands to a single request which is sent to the MCU. `detectorTowerAngleAxis->deferredMovementWait_` can be set with the IOC shell function `setDeferredMovementWait` and defaults to 100 ms.
|
||||
|
||||
The `detectorTowerController` is a thin wrapper around a `turboPmacController` which overwrites the `readInt32` and `writeInt32` in order to support the PVs "$(INSTR)$(M):ChangeState", "$(INSTR)$(M):PositionStateRBV" and "$(INSTR)$(M):ChangingStateRBV". Any calls to these two methods not concerning the aforementioned PVs are directly forwarded to `turboPmacController::readInt32` / `turboPmacController::writeInt32`.
|
||||
|
||||
In order to save on movement time, movement commands to auxiliary axes and the `detectorTowerAxis` are collected and then send as a single resulting movement command to the MCU. In order to do so, a "collector" thread is running which checks if a movement request has been send to one of the axes. If that is the case, it waits for some time and checks if commands for other axes are given as well. Then, it calls `detectorTowerAxis::startCombinedMove` which combines all commands to a single request which is sent to the MCU. This allows the user to start a combined movement e.g. via caput:
|
||||
|
||||
```
|
||||
caput tower 2 & caput liftZeroCorr 10
|
||||
```
|
||||
|
||||
When using the axis from NICOS, using the `maw` or `move` command with multiple devices has the same effect:
|
||||
|
||||
```
|
||||
move("tower", 2, "liftZeroCorr", 10)
|
||||
```
|
||||
The `detectorTowerController` is a thin wrapper around a `turboPmacController` which overwrites the `readInt32`, `writeInt32` and `writeFloat64` methods in order to support the additional PVs defined in `db/detectorTower.db`. Any calls to these two methods not concerning the aforementioned PVs are directly forwarded to `turboPmacController::readInt32` / `turboPmacController::writeInt32`.
|
||||
|
||||
### Versioning
|
||||
|
||||
@ -121,4 +128,4 @@ Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-e
|
||||
|
||||
### How to build it
|
||||
|
||||
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
|
||||
This driver can be compiled and installed by running `make install` from the same directory where the Makefile is located. However, since it uses the git submodule turboPmac, make sure that the correct version of the submodule repository is checked out AND the change is commited (`git status` shows no non-committed changes). Please see the section "Usage as static dependency" in https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md for more details.
|
@ -1,12 +1,3 @@
|
||||
# Set to 0 to move the tower into working state and to 1 to move into changer position
|
||||
record(longout, "$(INSTR)$(M):ChangeState") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) CHANGE_STATE")
|
||||
field(PINI, "NO")
|
||||
field(DRVH, "1")
|
||||
field(DRVL, "0")
|
||||
}
|
||||
|
||||
# Read the position state of the axis:
|
||||
# 0 = not ready
|
||||
# 1 = ready (in working position)
|
||||
@ -20,19 +11,122 @@ record(longin, "$(INSTR)$(M):PositionStateRBV")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
|
||||
# Set to 0 to move the tower into working state and to 1 to move into changer position.
|
||||
# The PV "$(INSTR)$(M):ChangeStateRBV" can be used to check if starting the corresponding
|
||||
# movement was successfull.
|
||||
record(longout, "$(INSTR)$(M):ChangeState") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) CHANGE_STATE")
|
||||
field(PINI, "NO")
|
||||
field(DRVH, "1")
|
||||
field(DRVL, "0")
|
||||
}
|
||||
|
||||
# When a value is written to the PV "$(INSTR)$(M):ChangeState", the tower needs to be
|
||||
# idle (PositionState = 0), otherwise the state will not be changed. If the value
|
||||
# of this PV is equal to "$(INSTR)$(M):ChangeState", starting a state change was
|
||||
# successfull, otherwise not.
|
||||
record(longin, "$(INSTR)$(M):ChangeStateRBV")
|
||||
{
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(CONTROLLER),$(AXIS)) CHANGE_STATE_RBV")
|
||||
field(SCAN, "I/O Intr")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
|
||||
# This PV combines the position state and the movement readback value from the
|
||||
# motor record:
|
||||
# - 0, if the tower is in working state or transitioning to change position
|
||||
# - 1, If the tower is in change position or transitioning to working state
|
||||
record(calc, "$(INSTR)$(M):ChangingStateRBV")
|
||||
{
|
||||
field(INPA, "$(INSTR)$(M).MOVN CP")
|
||||
field(INPB, "$(INSTR)$(M):PositionStateRBV CP")
|
||||
field(CALC, "(B == 1 || (B == 2 && A == 1)) ? 0 : 1")
|
||||
field(INPA, "$(INSTR)$(M):PositionStateRBV CP")
|
||||
field(INPB, "$(INSTR)$(M).MOVN CP")
|
||||
field(CALC, "((A == 2 && B == 0) || A == 3) ? 1 : 0")
|
||||
}
|
||||
|
||||
# Convert the double value from the calc record to an integer value
|
||||
# Convert the double value from the calc record to an integer value.
|
||||
record(longout, "$(INSTR)$(M):ChangingStateRBV_int") {
|
||||
field(DOL, "$(INSTR)$(M):ChangingStateRBV CP")
|
||||
field(OMSL, "closed_loop")
|
||||
}
|
||||
|
||||
# Read out the origin of the corresponding axis by the given value.
|
||||
# This PV does nothing for "normal" Turbo PMAC axes.
|
||||
record(ai, "$(INSTR)$(M):Origin") {
|
||||
field(DTYP, "asynFloat64")
|
||||
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_ORIGIN")
|
||||
field(SCAN, "I/O Intr")
|
||||
field(PINI, "NO")
|
||||
field(VAL, "0")
|
||||
}
|
||||
|
||||
# Shift the origin of the corresponding axis by the given value. The axis will move to this
|
||||
# position and the hardware controller will internally set this position as the
|
||||
# new "0" value. This PV does nothing for "normal" Turbo PMAC axes.
|
||||
record(ao, "$(INSTR)$(M):AdjustOrigin") {
|
||||
field(DTYP, "Raw Soft Channel")
|
||||
field(PINI, "NO")
|
||||
field(FLNK, "$(INSTR)$(M):ResetAO")
|
||||
field(VAL, "0")
|
||||
}
|
||||
|
||||
# Reset the PV $(INSTR)$(M):AdjustOrigin to zero after it has been written to and
|
||||
# forward the value to $(INSTR)$(M):WriteAO which does the actual writing.
|
||||
record(seq, "$(INSTR)$(M):ResetAO") {
|
||||
field(DESC, "Write value to hardware then reset to 0")
|
||||
field(DOL1, "$(INSTR)$(M):AdjustOrigin")
|
||||
field(LNK1, "$(INSTR)$(M):WriteAO.VAL PP") # Perform write to hardware
|
||||
field(DOL2, "0.0")
|
||||
field(LNK2, "$(INSTR)$(M):AdjustOrigin.VAL PP") # Reset to zero
|
||||
}
|
||||
|
||||
# This record forwards the adjustment of the origin to the asyn driver.
|
||||
record(ao, "$(INSTR)$(M):WriteAO") {
|
||||
field(DTYP, "asynFloat64")
|
||||
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ADJUST_ORIGIN")
|
||||
field(PINI, "NO")
|
||||
field(VAL, "0")
|
||||
}
|
||||
|
||||
# This record pair reads the parameter library value for "AdjustOriginHighLimitFromDriver_"
|
||||
# and pushes it to the high limit field of the "$(INSTR)$(M):AdjustOrigin" PV.
|
||||
# This can be used to read limits from the hardware and correspondingly update the motor record from the driver.
|
||||
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
|
||||
# This record is coupled to the parameter library via AdjustOriginHighLimitFromDriver_ -> MOTOR_AOHL_FROM_DRIVER.
|
||||
record(ai, "$(INSTR)$(M):AOHL_RBV")
|
||||
{
|
||||
field(DTYP, "asynFloat64")
|
||||
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_AOHL_FROM_DRIVER")
|
||||
field(SCAN, "I/O Intr")
|
||||
field(FLNK, "$(INSTR)$(M):PushAOHL2Field")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
# Abbreviated name because EPICS apparently has a maximum record name length
|
||||
record(ao, "$(INSTR)$(M):PushAOHL2Field") {
|
||||
field(DOL, "$(INSTR)$(M):AOHL_RBV NPP")
|
||||
field(OUT, "$(INSTR)$(M):AdjustOrigin.DRVH")
|
||||
field(OMSL, "closed_loop")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
|
||||
# This record pair reads the parameter library value for "motorLowLimitFromDriver_"
|
||||
# and pushes it to the low limit field of the "$(INSTR)$(M):AdjustOrigin" PV.
|
||||
# This can be used to read limits from the hardware and correspondingly update the motor record from the driver.
|
||||
# The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php.
|
||||
# This record is coupled to the parameter library via AdjustOriginLowLimitFromDriver_ -> MOTOR_AOLL_FROM_DRIVER.
|
||||
record(ai, "$(INSTR)$(M):AOLL_RBV")
|
||||
{
|
||||
field(DTYP, "asynFloat64")
|
||||
field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_AOLL_FROM_DRIVER")
|
||||
field(SCAN, "I/O Intr")
|
||||
field(FLNK, "$(INSTR)$(M):PushAOLL2Field")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
# Abbreviated name because EPICS apparently has a maximum record name length
|
||||
record(ao, "$(INSTR)$(M):PushAOLL2Field") {
|
||||
field(DOL, "$(INSTR)$(M):AOLL_RBV NPP")
|
||||
field(OUT, "$(INSTR)$(M):AdjustOrigin.DRVL")
|
||||
field(OMSL, "closed_loop")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
|
BIN
images/CoordinateSystems.odg
Normal file
BIN
images/CoordinateSystems.odg
Normal file
Binary file not shown.
447
images/CoordinateSystems.svg
Normal file
447
images/CoordinateSystems.svg
Normal file
@ -0,0 +1,447 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.2" width="215.9mm" height="80mm" viewBox="0 0 21590 8000" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
|
||||
<defs class="ClipPathGroup">
|
||||
<clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
|
||||
<rect x="0" y="0" width="21590" height="8000"/>
|
||||
</clipPath>
|
||||
<clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse">
|
||||
<rect x="21" y="8" width="21547" height="7984"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<defs>
|
||||
<font id="EmbeddedFont_1" horiz-adv-x="2048">
|
||||
<font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
|
||||
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
|
||||
<glyph unicode="α" horiz-adv-x="1060" d="M 843,237 C 801,146 751,81 693,41 634,0 565,-20 484,-20 347,-20 247,26 183,118 118,210 86,349 86,536 86,725 122,867 195,961 267,1055 373,1102 512,1102 596,1102 668,1080 728,1035 787,990 832,928 862,847 L 864,847 C 879,926 904,1005 937,1082 L 1125,1082 C 1092,1011 1061,925 1033,823 1005,720 988,636 983,571 986,464 998,360 1018,257 1037,154 1063,69 1094,0 L 911,0 C 895,41 881,85 870,133 858,180 850,215 847,237 L 843,237 Z M 275,542 C 275,394 294,287 332,220 370,153 433,119 521,119 601,119 669,158 724,235 779,312 815,415 832,546 813,687 778,792 727,863 676,934 611,969 532,969 441,969 376,936 336,869 295,802 275,693 275,542 Z"/>
|
||||
<glyph unicode="z" horiz-adv-x="848" d="M 83,0 L 83,137 688,943 117,943 117,1082 901,1082 901,945 295,139 922,139 922,0 83,0 Z"/>
|
||||
<glyph unicode="x" horiz-adv-x="1006" d="M 801,0 L 510,444 217,0 23,0 408,556 41,1082 240,1082 510,661 778,1082 979,1082 612,558 1002,0 801,0 Z"/>
|
||||
<glyph unicode="u" horiz-adv-x="874" d="M 314,1082 L 314,396 C 314,325 321,269 335,230 349,191 371,162 402,145 433,128 478,119 537,119 624,119 692,149 742,208 792,267 817,350 817,455 L 817,1082 997,1082 997,231 C 997,105 999,28 1003,0 L 833,0 C 832,3 832,12 831,27 830,42 830,59 829,78 828,97 826,132 825,185 L 822,185 C 781,110 733,58 679,27 624,-4 557,-20 476,-20 357,-20 271,10 216,69 161,128 133,225 133,361 L 133,1082 314,1082 Z"/>
|
||||
<glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 495,-8 434,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 474,127 509,132 554,141 L 554,8 Z"/>
|
||||
<glyph unicode="s" horiz-adv-x="901" d="M 950,299 C 950,197 912,118 835,63 758,8 650,-20 511,-20 376,-20 273,2 200,47 127,91 79,160 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 602,117 669,131 712,159 754,187 775,229 775,285 775,328 760,362 731,389 702,416 654,438 589,455 L 460,489 C 357,516 283,542 240,568 196,593 162,624 137,661 112,698 100,743 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 759,862 732,899 689,925 645,950 586,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,783 283,758 299,738 315,718 339,701 370,687 401,673 467,654 568,629 663,605 732,583 774,563 816,542 849,520 874,495 898,470 917,442 930,410 943,377 950,340 950,299 Z"/>
|
||||
<glyph unicode="r" horiz-adv-x="530" d="M 142,0 L 142,830 C 142,906 140,990 136,1082 L 306,1082 C 311,959 314,886 314,861 L 318,861 C 347,954 380,1017 417,1051 454,1085 507,1102 575,1102 599,1102 623,1099 648,1092 L 648,927 C 624,934 592,937 552,937 477,937 420,905 381,841 342,776 322,684 322,564 L 322,0 142,0 Z"/>
|
||||
<glyph unicode="p" horiz-adv-x="953" d="M 1053,546 C 1053,169 920,-20 655,-20 488,-20 376,43 319,168 L 314,168 C 317,163 318,106 318,-2 L 318,-425 138,-425 138,861 C 138,972 136,1046 132,1082 L 306,1082 C 307,1079 308,1070 309,1054 310,1037 312,1012 314,978 315,944 316,921 316,908 L 320,908 C 352,975 394,1024 447,1055 500,1086 569,1101 655,1101 788,1101 888,1056 954,967 1020,878 1053,737 1053,546 Z M 864,542 C 864,693 844,800 803,865 762,930 698,962 609,962 538,962 482,947 442,917 401,887 371,840 350,777 329,713 318,630 318,528 318,386 341,281 386,214 431,147 505,113 607,113 696,113 762,146 803,212 844,277 864,387 864,542 Z"/>
|
||||
<glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 407,-20 288,28 207,125 126,221 86,360 86,542 86,915 248,1102 571,1102 736,1102 858,1057 936,966 1014,875 1053,733 1053,542 Z M 864,542 C 864,691 842,800 798,868 753,935 679,969 574,969 469,969 393,935 346,866 299,797 275,689 275,542 275,399 298,292 345,221 391,149 464,113 563,113 671,113 748,148 795,217 841,286 864,395 864,542 Z"/>
|
||||
<glyph unicode="n" horiz-adv-x="874" d="M 825,0 L 825,686 C 825,757 818,813 804,852 790,891 768,920 737,937 706,954 661,963 602,963 515,963 447,933 397,874 347,815 322,732 322,627 L 322,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 358,972 406,1025 461,1056 515,1087 582,1102 663,1102 782,1102 869,1073 924,1014 979,955 1006,857 1006,721 L 1006,0 825,0 Z"/>
|
||||
<glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/>
|
||||
<glyph unicode="i" horiz-adv-x="187" d="M 137,1312 L 137,1484 317,1484 317,1312 137,1312 Z M 137,0 L 137,1082 317,1082 317,0 137,0 Z"/>
|
||||
<glyph unicode="f" horiz-adv-x="557" d="M 361,951 L 361,0 181,0 181,951 29,951 29,1082 181,1082 181,1204 C 181,1303 203,1374 246,1417 289,1460 356,1482 445,1482 495,1482 537,1478 572,1470 L 572,1333 C 542,1338 515,1341 492,1341 446,1341 413,1329 392,1306 371,1283 361,1240 361,1179 L 361,1082 572,1082 572,951 361,951 Z"/>
|
||||
<glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,379 302,283 353,216 404,149 479,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 954,65 807,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,727 129,864 213,959 296,1054 416,1102 571,1102 889,1102 1048,910 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 481,969 412,940 361,882 310,823 282,743 278,641 L 862,641 Z"/>
|
||||
<glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,123 87,202 87,302 87,414 124,500 198,560 271,620 390,652 554,656 L 797,660 797,719 C 797,807 778,870 741,908 704,946 645,965 565,965 484,965 426,951 389,924 352,897 330,853 323,793 L 135,810 C 166,1005 310,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1097,111 1117,113 1139,118 L 1139,6 C 1094,-5 1047,-10 1000,-10 933,-10 885,8 855,43 824,78 807,132 803,207 L 797,207 C 751,124 698,66 637,32 576,-3 501,-20 414,-20 Z M 455,115 C 521,115 580,130 631,160 682,190 723,231 753,284 782,336 797,390 797,445 L 797,534 600,530 C 515,529 451,520 408,504 364,488 330,463 307,430 284,397 272,353 272,299 272,240 288,195 320,163 351,131 396,115 455,115 Z"/>
|
||||
<glyph unicode="C" horiz-adv-x="1324" d="M 792,1274 C 636,1274 515,1224 428,1124 341,1023 298,886 298,711 298,538 343,400 434,295 524,190 646,137 800,137 997,137 1146,235 1245,430 L 1401,352 C 1343,231 1262,138 1157,75 1052,12 930,-20 791,-20 649,-20 526,10 423,69 319,128 240,212 186,322 131,431 104,561 104,711 104,936 165,1112 286,1239 407,1366 575,1430 790,1430 940,1430 1065,1401 1166,1342 1267,1283 1341,1196 1388,1081 L 1207,1021 C 1174,1103 1122,1166 1050,1209 977,1252 891,1274 792,1274 Z"/>
|
||||
<glyph unicode=" " horiz-adv-x="556"/>
|
||||
</font>
|
||||
</defs>
|
||||
<defs class="TextShapeIndex">
|
||||
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12 id13 id14 id15 id16 id17 id18"/>
|
||||
</defs>
|
||||
<defs class="EmbeddedBulletChars">
|
||||
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
|
||||
</g>
|
||||
<g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
|
||||
<path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
|
||||
</g>
|
||||
</defs>
|
||||
<g>
|
||||
<g id="id2" class="Master_Slide">
|
||||
<g id="bg-id2" class="Background"/>
|
||||
<g id="bo-id2" class="BackgroundObjects"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="SlideGroup">
|
||||
<g>
|
||||
<g id="container-id1">
|
||||
<g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
|
||||
<g class="Page">
|
||||
<g class="com.sun.star.drawing.CustomShape">
|
||||
<g id="id3">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="2447" y="2002" width="16587" height="3309"/>
|
||||
<path fill="rgb(204,204,204)" stroke="none" d="M 10705,3853 L 2448,2397 2517,2003 19032,4915 18963,5309 10705,3853 Z"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" d="M 10705,3853 L 2448,2397 2517,2003 19032,4915 18963,5309 10705,3853 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.LineShape">
|
||||
<g id="id4">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1982" y="2082" width="17037" height="3037"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2000,2100 L 2207,2136"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2310,2155 L 2345,2161"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2448,2179 L 2655,2216"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2758,2234 L 2793,2240"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2896,2258 L 3103,2295"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3206,2313 L 3241,2319"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3344,2337 L 3551,2374"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3654,2392 L 3689,2398"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3792,2416 L 3999,2453"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4103,2471 L 4137,2477"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4240,2495 L 4447,2532"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4551,2550 L 4585,2556"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4688,2574 L 4895,2611"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4999,2629 L 5033,2635"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5137,2654 L 5343,2690"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5447,2708 L 5481,2714"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5585,2733 L 5791,2769"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5895,2787 L 5929,2793"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6033,2812 L 6239,2848"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6343,2866 L 6377,2872"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6481,2891 L 6688,2927"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6791,2945 L 6825,2952"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6929,2970 L 7136,3006"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7239,3025 L 7274,3031"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7377,3049 L 7584,3085"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7687,3104 L 7722,3110"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7825,3128 L 8032,3164"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 8135,3183 L 8170,3189"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 8273,3207 L 8480,3244"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 8583,3262 L 8618,3268"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 8721,3286 L 8928,3323"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9031,3341 L 9066,3347"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9169,3365 L 9376,3402"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9479,3420 L 9514,3426"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9617,3444 L 9824,3481"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9928,3499 L 9962,3505"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10065,3523 L 10272,3560"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10376,3578 L 10410,3584"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10513,3602 L 10720,3639"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10824,3657 L 10858,3663"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10962,3681 L 11168,3718"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 11272,3736 L 11306,3742"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 11410,3761 L 11616,3797"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 11720,3815 L 11754,3821"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 11858,3840 L 12064,3876"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12168,3894 L 12202,3900"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12306,3919 L 12513,3955"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12616,3973 L 12650,3979"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12754,3998 L 12961,4034"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13064,4052 L 13099,4059"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13202,4077 L 13409,4113"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13512,4132 L 13547,4138"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13650,4156 L 13857,4192"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13960,4211 L 13995,4217"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14098,4235 L 14305,4271"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14408,4290 L 14443,4296"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14546,4314 L 14753,4351"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14856,4369 L 14891,4375"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14994,4393 L 15201,4430"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15304,4448 L 15339,4454"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15442,4472 L 15649,4509"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15753,4527 L 15787,4533"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15890,4551 L 16097,4588"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 16201,4606 L 16235,4612"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 16338,4630 L 16545,4667"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 16649,4685 L 16683,4691"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 16787,4709 L 16993,4746"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17097,4764 L 17131,4770"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17235,4788 L 17441,4825"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17545,4843 L 17579,4849"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17683,4868 L 17889,4904"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17993,4922 L 18027,4928"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 18131,4947 L 18338,4983"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 18441,5001 L 18475,5007"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 18579,5026 L 18786,5062"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 18889,5080 L 18924,5087"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.LineShape">
|
||||
<g id="id5">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="2324" y="2200" width="353" height="3819"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="35" stroke-linejoin="round" d="M 2500,6000 L 2500,2705"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 2500,2200 L 2324,2728 2676,2728 2500,2200 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.LineShape">
|
||||
<g id="id6">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1982" y="5082" width="17037" height="37"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2000,5100 L 2210,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2315,5100 L 2350,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2455,5100 L 2665,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2770,5100 L 2805,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 2910,5100 L 3120,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3225,5100 L 3260,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3365,5100 L 3575,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3680,5100 L 3715,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3820,5100 L 4030,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4135,5100 L 4170,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4275,5100 L 4485,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4590,5100 L 4625,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 4730,5100 L 4940,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5045,5100 L 5080,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5185,5100 L 5395,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5500,5100 L 5535,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5640,5100 L 5850,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 5955,5100 L 5990,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6095,5100 L 6305,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6410,5100 L 6445,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6550,5100 L 6760,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 6865,5100 L 6900,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7005,5100 L 7215,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7320,5100 L 7355,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7460,5100 L 7670,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7775,5100 L 7810,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 7915,5100 L 8125,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 8230,5100 L 8265,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 8370,5100 L 8580,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 8685,5100 L 8720,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 8825,5100 L 9035,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9140,5100 L 9175,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9280,5100 L 9490,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9595,5100 L 9630,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9735,5100 L 9945,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10050,5100 L 10085,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10190,5100 L 10400,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10505,5100 L 10540,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10645,5100 L 10855,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 10960,5100 L 10995,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 11100,5100 L 11310,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 11415,5100 L 11450,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 11555,5100 L 11765,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 11870,5100 L 11905,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12010,5100 L 12220,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12325,5100 L 12360,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12465,5100 L 12675,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12780,5100 L 12815,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 12920,5100 L 13130,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13235,5100 L 13270,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13375,5100 L 13585,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13690,5100 L 13725,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 13830,5100 L 14040,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14145,5100 L 14180,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14285,5100 L 14495,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14600,5100 L 14635,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 14740,5100 L 14950,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15055,5100 L 15090,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15195,5100 L 15405,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15510,5100 L 15545,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15650,5100 L 15860,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 15965,5100 L 16000,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 16105,5100 L 16315,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 16420,5100 L 16455,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 16560,5100 L 16770,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 16875,5100 L 16910,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17015,5100 L 17225,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17330,5100 L 17365,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17470,5100 L 17680,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17785,5100 L 17820,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 17925,5100 L 18135,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 18240,5100 L 18275,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 18380,5100 L 18590,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 18695,5100 L 18730,5100"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="35" stroke-linejoin="round" d="M 18835,5100 L 19000,5100"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.LineShape">
|
||||
<g id="id7">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="18824" y="5100" width="353" height="919"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="35" stroke-linejoin="round" d="M 19000,6000 L 19000,5605"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 19000,5100 L 18824,5628 19176,5628 19000,5100 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.LineShape">
|
||||
<g id="id8">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1582" y="5824" width="919" height="353"/>
|
||||
<path fill="none" stroke="rgb(52,101,164)" stroke-width="35" stroke-linejoin="round" d="M 1600,6000 L 1995,6000"/>
|
||||
<path fill="rgb(52,101,164)" stroke="none" d="M 2500,6000 L 1972,5824 1972,6176 2500,6000 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.OpenBezierShape">
|
||||
<g id="id9">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="3374" y="2399" width="449" height="2703"/>
|
||||
<path fill="none" stroke="rgb(255,0,0)" stroke-width="35" stroke-linejoin="round" d="M 3500,5083 C 3501,3200 3594,3011 3653,2645"/>
|
||||
<path fill="rgb(255,0,0)" stroke="none" d="M 3626,5100 L 3542,5100 3458,5100 3374,5100 3374,5089 3374,5078 3374,5067 3458,5067 3542,5067 3626,5067 3626,5078 3626,5089 3626,5100 Z"/>
|
||||
<path fill="rgb(255,0,0)" stroke="none" d="M 3700,2400 L 3475,2644 3821,2708 3700,2400 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.TextShape">
|
||||
<g id="id10">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="3600" y="3576" width="2001" height="925"/>
|
||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="3850" y="4150"><tspan fill="rgb(255,0,0)" stroke="none">α</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.LineShape">
|
||||
<g id="id11">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="8924" y="5100" width="353" height="903"/>
|
||||
<path fill="none" stroke="rgb(255,0,0)" stroke-width="35" stroke-linejoin="round" d="M 9100,5984 L 9100,5357"/>
|
||||
<path fill="rgb(255,0,0)" stroke="none" d="M 9226,6000 L 9142,6000 9058,6000 8974,6000 8974,5989 8974,5978 8974,5967 9058,5967 9142,5967 9226,5967 9226,5978 9226,5989 9226,6000 Z"/>
|
||||
<path fill="rgb(255,0,0)" stroke="none" d="M 9100,5100 L 8924,5381 9276,5381 9100,5100 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.TextShape">
|
||||
<g id="id12">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="9200" y="5076" width="1301" height="925"/>
|
||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="287px" font-weight="400"><tspan class="TextPosition" x="9450" y="5650"><tspan font-size="494px" fill="rgb(255,0,0)" stroke="none">z</tspan></tspan><tspan class="TextPosition" x="9700" y="5813"><tspan fill="rgb(255,0,0)" stroke="none">lift</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.LineShape">
|
||||
<g id="id13">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1991" y="5991" width="17019" height="19"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 2000,6000 L 2108,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 2162,6000 L 2270,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 2324,6000 L 2432,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 2486,6000 L 2594,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 2648,6000 L 2756,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 2810,6000 L 2918,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 2972,6000 L 3080,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 3134,6000 L 3242,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 3296,6000 L 3404,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 3458,6000 L 3566,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 3620,6000 L 3728,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 3782,6000 L 3890,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 3944,6000 L 4052,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4106,6000 L 4214,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4268,6000 L 4376,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4430,6000 L 4538,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4592,6000 L 4700,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4754,6000 L 4862,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4916,6000 L 5024,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 5078,6000 L 5186,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 5240,6000 L 5348,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 5402,6000 L 5510,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 5564,6000 L 5672,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 5726,6000 L 5834,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 5888,6000 L 5996,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6050,6000 L 6158,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6212,6000 L 6320,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6374,6000 L 6482,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6536,6000 L 6644,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6698,6000 L 6806,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6860,6000 L 6968,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 7022,6000 L 7130,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 7184,6000 L 7292,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 7346,6000 L 7454,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 7508,6000 L 7616,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 7670,6000 L 7778,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 7832,6000 L 7940,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 7994,6000 L 8102,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 8156,6000 L 8264,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 8318,6000 L 8426,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 8480,6000 L 8588,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 8642,6000 L 8750,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 8804,6000 L 8912,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 8966,6000 L 9074,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 9128,6000 L 9236,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 9290,6000 L 9398,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 9452,6000 L 9560,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 9614,6000 L 9722,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 9776,6000 L 9884,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 9938,6000 L 10046,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 10100,6000 L 10208,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 10262,6000 L 10370,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 10424,6000 L 10532,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 10586,6000 L 10694,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 10748,6000 L 10856,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 10910,6000 L 11018,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 11072,6000 L 11180,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 11234,6000 L 11342,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 11396,6000 L 11504,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 11558,6000 L 11666,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 11720,6000 L 11828,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 11882,6000 L 11990,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 12044,6000 L 12152,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 12206,6000 L 12314,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 12368,6000 L 12476,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 12530,6000 L 12638,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 12692,6000 L 12800,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 12854,6000 L 12962,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 13016,6000 L 13124,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 13178,6000 L 13286,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 13340,6000 L 13448,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 13502,6000 L 13610,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 13664,6000 L 13772,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 13826,6000 L 13934,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 13988,6000 L 14096,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 14150,6000 L 14258,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 14312,6000 L 14420,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 14474,6000 L 14582,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 14636,6000 L 14744,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 14798,6000 L 14906,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 14960,6000 L 15068,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 15122,6000 L 15230,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 15284,6000 L 15392,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 15446,6000 L 15554,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 15608,6000 L 15716,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 15770,6000 L 15878,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 15932,6000 L 16040,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 16094,6000 L 16202,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 16256,6000 L 16364,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 16418,6000 L 16526,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 16580,6000 L 16688,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 16742,6000 L 16850,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 16904,6000 L 17012,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 17066,6000 L 17174,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 17228,6000 L 17336,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 17390,6000 L 17498,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 17552,6000 L 17660,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 17714,6000 L 17822,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 17876,6000 L 17984,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 18038,6000 L 18146,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 18200,6000 L 18308,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 18362,6000 L 18470,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 18524,6000 L 18632,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 18686,6000 L 18794,6000"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 18848,6000 L 18956,6000"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.TextShape">
|
||||
<g id="id14">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1700" y="2100" width="1101" height="1017"/>
|
||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="1950" y="2674"><tspan fill="rgb(42,96,153)" stroke="none">z</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.TextShape">
|
||||
<g id="id15">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="1700" y="5094" width="701" height="807"/>
|
||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="494px" font-weight="400"><tspan class="TextPosition" x="1950" y="5668"><tspan fill="rgb(42,96,153)" stroke="none">x</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.TextShape">
|
||||
<g id="id16">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="17400" y="5100" width="1801" height="925"/>
|
||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="287px" font-weight="400"><tspan class="TextPosition" x="17650" y="5674"><tspan font-size="494px" fill="rgb(42,96,153)" stroke="none">z</tspan></tspan><tspan class="TextPosition" x="17900" y="5837"><tspan fill="rgb(42,96,153)" stroke="none">support</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.LineShape">
|
||||
<g id="id17">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="18499" y="3299" width="527" height="1802"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" d="M 18500,3300 L 18579,3586"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" d="M 18615,3715 L 18695,4001"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" d="M 18731,4131 L 18810,4417"/>
|
||||
<path fill="none" stroke="rgb(0,0,0)" d="M 18846,4546 L 18885,4686"/>
|
||||
<path fill="rgb(0,0,0)" stroke="none" d="M 19000,5100 L 19024,4626 18735,4707 19000,5100 Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g class="com.sun.star.drawing.TextShape">
|
||||
<g id="id18">
|
||||
<rect class="BoundingBox" stroke="none" fill="none" x="17000" y="2561" width="2001" height="1040"/>
|
||||
<text class="TextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="353px" font-weight="400"><tspan class="TextPosition" x="17250" y="3004"><tspan fill="rgb(0,0,0)" stroke="none">Center of </tspan></tspan><tspan class="TextPosition" x="17250" y="3398"><tspan fill="rgb(0,0,0)" stroke="none">rotation</tspan></tspan></tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 48 KiB |
@ -1,241 +0,0 @@
|
||||
#include "auxiliaryAxis.h"
|
||||
#include "detectorTowerAxis.h"
|
||||
#include "detectorTowerController.h"
|
||||
#include "turboPmacController.h"
|
||||
#include <epicsExport.h>
|
||||
#include <errlog.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
/*
|
||||
Contains all instances of auxiliaryAxis which have been created and is used
|
||||
in the initialization hook function.
|
||||
*/
|
||||
static std::vector<auxiliaryAxis *> axes;
|
||||
|
||||
/**
|
||||
* @brief Hook function to perform certain actions during the IOC initialization
|
||||
*
|
||||
* @param iState
|
||||
*/
|
||||
static void epicsInithookFunction(initHookState iState) {
|
||||
if (iState == initHookAfterDatabaseRunning) {
|
||||
// Iterate through all axes of each and call the initialization method
|
||||
// on each one of them.
|
||||
for (std::vector<auxiliaryAxis *>::iterator itA = axes.begin();
|
||||
itA != axes.end(); ++itA) {
|
||||
auxiliaryAxis *axis = *itA;
|
||||
axis->init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auxiliaryAxis::auxiliaryAxis(detectorTowerController *pC, int axisNo)
|
||||
: turboPmacAxis(pC, axisNo, false), pC_(pC) {
|
||||
|
||||
/*
|
||||
The superclass constructor sinqAxis calls in turn its superclass constructor
|
||||
asynMotorAxis. In the latter, a pointer to the constructed object this is
|
||||
stored inside the array pAxes_:
|
||||
|
||||
pC->pAxes_[axisNo] = this;
|
||||
|
||||
Therefore, the axes are managed by the controller pC. If axisNo is out of
|
||||
bounds, asynMotorAxis prints an error (see
|
||||
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()) {
|
||||
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d: FATAL ERROR: "
|
||||
"Axis index %d must be smaller than the total number of axes "
|
||||
"%d",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
axisNo_, pC->numAxes());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Register the hook function during construction of the first axis object
|
||||
if (axes.empty()) {
|
||||
initHookRegister(&epicsInithookFunction);
|
||||
}
|
||||
|
||||
// Collect all axes into this list which will be used in the hook
|
||||
// function
|
||||
axes.push_back(this);
|
||||
}
|
||||
|
||||
auxiliaryAxis::~auxiliaryAxis(void) {
|
||||
// Since the controller memory is managed somewhere else, we don't need to
|
||||
// clean up the pointer pC here.
|
||||
}
|
||||
|
||||
asynStatus auxiliaryAxis::init() {
|
||||
|
||||
// Local variable declaration
|
||||
asynStatus status = asynSuccess;
|
||||
double motorRecResolution = 0.0;
|
||||
|
||||
// The parameter library takes some time to be initialized. Therefore we
|
||||
// wait until the status is not asynParamUndefined anymore.
|
||||
time_t now = time(NULL);
|
||||
time_t maxInitTime = 60;
|
||||
while (1) {
|
||||
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||
&motorRecResolution);
|
||||
if (status == asynParamUndefined) {
|
||||
if (now + maxInitTime < time(NULL)) {
|
||||
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d\nInitializing the parameter library failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
return asynError;
|
||||
}
|
||||
} else if (status == asynSuccess) {
|
||||
break;
|
||||
} else if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorRecResolution_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// Perform the actual poll
|
||||
asynStatus auxiliaryAxis::doPoll(bool *moving) {
|
||||
|
||||
// Return value for the poll
|
||||
asynStatus poll_status = asynSuccess;
|
||||
|
||||
// Status of parameter library operations
|
||||
asynStatus pl_status = asynSuccess;
|
||||
|
||||
int isMoving = 0;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
/*
|
||||
The axis is moving if the detectorTowerAxis is moving -> We read the moving
|
||||
status from the detectorTowerAxis.
|
||||
*/
|
||||
pl_status = pC_->getIntegerParam(dTA_->axisNo_, pC_->motorStatusMoving(),
|
||||
&isMoving);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_",
|
||||
dTA_->axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
*moving = isMoving != 0;
|
||||
|
||||
// Update the parameter library
|
||||
if (dTA_->error_ != 0) {
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
if (*moving == false) {
|
||||
pl_status = setIntegerParam(pC_->motorMoveToHome(), 0);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorMoveToHome_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// According to the function documentation of asynMotorAxis::poll, this
|
||||
// function should be called at the end of a poll implementation.
|
||||
pl_status = callParamCallbacks();
|
||||
bool wantToPrint = pl_status != asynSuccess;
|
||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, wantToPrint,
|
||||
pC_->asynUserSelf())) {
|
||||
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d:\ncallParamCallbacks failed with %s.%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
pC_->stringifyAsynStatus(poll_status),
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
}
|
||||
if (wantToPrint) {
|
||||
poll_status = pl_status;
|
||||
}
|
||||
|
||||
// The limits are written into this class instance inside the doPoll
|
||||
// function of detectorTowerAxis
|
||||
return poll_status;
|
||||
}
|
||||
|
||||
asynStatus auxiliaryAxis::doMove(double position, int relative,
|
||||
double min_velocity, double max_velocity,
|
||||
double acceleration) {
|
||||
|
||||
double motorRecResolution = 0.0;
|
||||
asynStatus pl_status = pC_->getDoubleParam(
|
||||
axisNo_, pC_->motorRecResolution(), &motorRecResolution);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
// Signal to the deferredMovementCollectorLoop (of the detectorTowerAxis)
|
||||
// that a movement should be started to the defined target position.
|
||||
targetPosition_ = position * motorRecResolution;
|
||||
dTA_->receivedTarget_ = true;
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus auxiliaryAxis::stop(double acceleration) {
|
||||
|
||||
// Status of read-write-operations of ASCII commands to the controller
|
||||
asynStatus rw_status = asynSuccess;
|
||||
|
||||
// Status of parameter library operations
|
||||
asynStatus pl_status = asynSuccess;
|
||||
|
||||
char response[pC_->MAXBUF_];
|
||||
|
||||
// =========================================================================
|
||||
|
||||
rw_status = pC_->writeRead(axisNo_, "P350=8", response, 0);
|
||||
|
||||
if (rw_status != asynSuccess) {
|
||||
asynPrint(
|
||||
pC_->asynUserSelf(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
|
||||
"failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
return asynError;
|
||||
}
|
||||
|
||||
return rw_status;
|
||||
}
|
||||
|
||||
asynStatus auxiliaryAxis::reset() { return dTA_->reset(); };
|
@ -1,81 +0,0 @@
|
||||
#ifndef auxiliaryAxis_H
|
||||
#define auxiliaryAxis_H
|
||||
#include "turboPmacAxis.h"
|
||||
|
||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||
// between the controller and the axis .h-file. See
|
||||
// https://en.cppreference.com/w/cpp/language/class.
|
||||
class detectorTowerController;
|
||||
class detectorTowerAxis;
|
||||
|
||||
class auxiliaryAxis : public turboPmacAxis {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new detectorTowerAxis
|
||||
*
|
||||
* @param pController Pointer to the associated controller
|
||||
* @param axisNo Index of the axis
|
||||
*/
|
||||
auxiliaryAxis(detectorTowerController *pController, int axisNo);
|
||||
|
||||
/**
|
||||
* @brief Destroy the turboPmacAxis
|
||||
*
|
||||
*/
|
||||
virtual ~auxiliaryAxis();
|
||||
|
||||
/**
|
||||
* @brief Readout of some values from the controller at IOC startup
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus init();
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `stop` function from asynMotorAxis
|
||||
*
|
||||
* @param acceleration Acceleration ACCEL from the motor record. This
|
||||
* value is currently not used.
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus stop(double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `doPoll` function from sinqAxis. The
|
||||
* parameters are described in the documentation of `sinqAxis::doPoll`.
|
||||
*
|
||||
* @param moving
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doPoll(bool *moving);
|
||||
|
||||
/**
|
||||
* @brief Set the target value for the detector angle and trigger a position
|
||||
* collection cycle
|
||||
*
|
||||
* @param position
|
||||
* @param relative
|
||||
* @param min_velocity
|
||||
* @param max_velocity
|
||||
* @param acceleration
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doMove(double position, int relative, double min_velocity,
|
||||
double max_velocity, double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Call the reset function of the associated `detectorTowerAxis`.
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus reset();
|
||||
|
||||
protected:
|
||||
detectorTowerController *pC_;
|
||||
detectorTowerAxis *dTA_;
|
||||
|
||||
private:
|
||||
friend class detectorTowerAxis;
|
||||
};
|
||||
|
||||
#endif
|
717
src/detectorTowerAngleAxis.cpp
Normal file
717
src/detectorTowerAngleAxis.cpp
Normal file
@ -0,0 +1,717 @@
|
||||
#include "detectorTowerAngleAxis.h"
|
||||
#include "detectorTowerController.h"
|
||||
#include "turboPmacController.h"
|
||||
#include <epicsExport.h>
|
||||
#include <errlog.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
/*
|
||||
Contains all instances of detectorTowerAngleAxis which have been created and is
|
||||
used in the initialization hook function.
|
||||
*/
|
||||
static std::vector<detectorTowerAngleAxis *> axes;
|
||||
|
||||
/**
|
||||
* @brief Hook function to perform certain actions during the IOC initialization
|
||||
*
|
||||
* @param iState
|
||||
*/
|
||||
static void epicsInithookFunction(initHookState iState) {
|
||||
if (iState == initHookAfterDatabaseRunning) {
|
||||
// Iterate through all axes of each and call the initialization method
|
||||
// on each one of them.
|
||||
for (std::vector<detectorTowerAngleAxis *>::iterator itA = axes.begin();
|
||||
itA != axes.end(); ++itA) {
|
||||
detectorTowerAngleAxis *axis = *itA;
|
||||
axis->init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void deferredMovementCollectorLoop(void *drvPvt) {
|
||||
detectorTowerAngleAxis *axis = (detectorTowerAngleAxis *)drvPvt;
|
||||
while (1) {
|
||||
if (axis->receivedTarget_) {
|
||||
// Wait for 100 ms and then start the movement with the information
|
||||
// available
|
||||
axis->startingDeferredMovement_ = true;
|
||||
epicsThreadSleep(axis->deferredMovementWait_);
|
||||
axis->startCombinedMove();
|
||||
|
||||
// After the movement command has been send, reset the flag
|
||||
axis->receivedTarget_ = false;
|
||||
}
|
||||
// Limit this loop to an idle frequency of 1 kHz
|
||||
epicsThreadSleep(0.001);
|
||||
}
|
||||
}
|
||||
|
||||
detectorTowerAngleAxis::detectorTowerAngleAxis(detectorTowerController *pC,
|
||||
int axisNo)
|
||||
: turboPmacAxis(pC, axisNo, false), pC_(pC) {
|
||||
|
||||
/*
|
||||
The superclass constructor sinqAxis calls in turn its superclass constructor
|
||||
asynMotorAxis. In the latter, a pointer to the constructed object this is
|
||||
stored inside the array pAxes_:
|
||||
|
||||
pC->pAxes_[axisNo] = this;
|
||||
|
||||
Therefore, the axes are managed by the controller pC. If axisNo is out of
|
||||
bounds, asynMotorAxis prints an error (see
|
||||
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()) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d: FATAL ERROR: "
|
||||
"Axis index %d must be smaller than the total number of axes "
|
||||
"%d",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
axisNo_, pC->numAxes());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Initialize all member variables
|
||||
// Assumed to be not ready by default, this is overwritten in the next poll
|
||||
error_ = 0;
|
||||
receivedTarget_ = false;
|
||||
startingDeferredMovement_ = false;
|
||||
deferredMovementWait_ = 0.1; // seconds
|
||||
|
||||
// Will be populated in the init() method
|
||||
beamRadius_ = 0.0;
|
||||
|
||||
// Register the hook function during construction of the first axis object
|
||||
if (axes.empty()) {
|
||||
initHookRegister(&epicsInithookFunction);
|
||||
}
|
||||
|
||||
// Collect all axes into this list which will be used in the hook
|
||||
// function
|
||||
axes.push_back(this);
|
||||
|
||||
// Create a thread which collects all movement commands send to components
|
||||
// of the virtual axis. After a component received a new target position,
|
||||
// it forwards this information to the thread. The thread then waits a short
|
||||
// time to see if other components also received new targets and starts the
|
||||
// movement with all targets afterwards.
|
||||
epicsThreadCreate("deferredMovement", epicsThreadPriorityLow,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||
(EPICSTHREADFUNC)deferredMovementCollectorLoop,
|
||||
(void *)this);
|
||||
}
|
||||
|
||||
detectorTowerAngleAxis::~detectorTowerAngleAxis(void) {
|
||||
// Since the controller memory is managed somewhere else, we don't need to
|
||||
// clean up the pointer pC here.
|
||||
}
|
||||
|
||||
asynStatus detectorTowerAngleAxis::init() {
|
||||
|
||||
// Local variable declaration
|
||||
asynStatus status = asynSuccess;
|
||||
double motorRecResolution = 0.0;
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int positionState = 0;
|
||||
int nvals = 0;
|
||||
|
||||
// The parameter library takes some time to be initialized. Therefore we
|
||||
// wait until the status is not asynParamUndefined anymore.
|
||||
time_t now = time(NULL);
|
||||
time_t maxInitTime = 60;
|
||||
while (1) {
|
||||
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||
&motorRecResolution);
|
||||
if (status == asynParamUndefined) {
|
||||
if (now + maxInitTime < time(NULL)) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis "
|
||||
"%ddeferredMovementCollectorLoop => %s, line "
|
||||
"%d\nInitializing the parameter library failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
return asynError;
|
||||
}
|
||||
} else if (status == asynSuccess) {
|
||||
break;
|
||||
} else if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorRecResolution_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the detector radius
|
||||
const char *command = "P459 P358";
|
||||
status = pC_->writeRead(axisNo_, command, response, 2);
|
||||
if (status != asynSuccess) {
|
||||
return status;
|
||||
}
|
||||
|
||||
nvals = sscanf(response, "%lf %d", &beamRadius_, &positionState);
|
||||
if (nvals != 2) {
|
||||
return pC_->couldNotParseResponse(command, response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// Initialize the motorStatusMoving flag
|
||||
status = setIntegerParam(pC_->motorStatusMoving(), 0);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusMoving_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
status = setIntegerParam(pC_->changeStateRBV(), positionState == 2);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "changeStateRBV", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
return callParamCallbacks();
|
||||
}
|
||||
|
||||
// Perform the actual poll
|
||||
asynStatus detectorTowerAngleAxis::poll(bool *moving) {
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Is this axis the one with the smallest index?
|
||||
// If not, just read out the movement status and update *moving
|
||||
if (axisNo() < liftAxis()->axisNo() && axisNo() < supportAxis()->axisNo()) {
|
||||
status = pC_->pollDetectorAxes(moving, this, liftAxis(), supportAxis());
|
||||
} else {
|
||||
status = pC_->getIntegerParam(axisNo(), pC_->motorStatusMoving(),
|
||||
(int *)moving);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusMoving",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
wasMoving_ = *moving;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerAngleAxis::doPoll(bool *moving) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nThe doPoll method "
|
||||
"of this axis type should not be reachable. This is a bug.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerAngleAxis::doMove(double position, int relative,
|
||||
double min_velocity,
|
||||
double max_velocity,
|
||||
double acceleration) {
|
||||
double motorRecResolution = 0.0;
|
||||
asynStatus plStatus = pC_->getDoubleParam(
|
||||
axisNo_, pC_->motorRecResolution(), &motorRecResolution);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "motorRecResolution_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
// Signal to the deferredMovementCollectorLoop that a movement should be
|
||||
// started to the defined target position.
|
||||
targetPosition_ = position * motorRecResolution;
|
||||
receivedTarget_ = true;
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerAngleAxis::startCombinedMove() {
|
||||
|
||||
// Status of read-write-operations of ASCII commands to the controller
|
||||
asynStatus rwStatus = asynSuccess;
|
||||
|
||||
// Status of parameter library operations
|
||||
asynStatus plStatus = asynSuccess;
|
||||
|
||||
char command[pC_->MAXBUF_] = {0};
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
double motorCoordinatesPosition = 0.0;
|
||||
int positionState = 0;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
plStatus =
|
||||
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "positionStateRBV_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// If the axis is in changer position, it must be moved into working
|
||||
// position before any move can be started.
|
||||
bool isInChangerPos = positionState == 2 || positionState == 3;
|
||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
isInChangerPos, pC_->pasynUser())) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
|
||||
"moved because it is moving from working to changer "
|
||||
"position, is in changer position or is moving from changer "
|
||||
"to working position.%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
}
|
||||
if (isInChangerPos) {
|
||||
|
||||
plStatus = setStringParam(pC_->motorMessageText(),
|
||||
"Move the axis to working state first.");
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "motorMessageText",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
plStatus = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
startingDeferredMovement_ = false;
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Set the target positions for beam tilt, detector tilt offset and lift
|
||||
// offset
|
||||
snprintf(command, sizeof(command), "Q451=%lf Q454=%lf P350=1",
|
||||
targetPosition_, liftAxis_->targetPosition_);
|
||||
|
||||
// Lock the access to the controller since this function runs in another
|
||||
// thread than the poll method.
|
||||
pC_->lock();
|
||||
|
||||
// We don't expect an answer
|
||||
rwStatus = pC_->writeRead(axisNo_, command, response, 0);
|
||||
|
||||
// Free the controller again
|
||||
pC_->unlock();
|
||||
|
||||
if (rwStatus != asynSuccess) {
|
||||
asynPrint(
|
||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nStarting movement to "
|
||||
"target position %lf failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
motorCoordinatesPosition);
|
||||
plStatus = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
callParamCallbacks();
|
||||
}
|
||||
|
||||
return rwStatus;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerAngleAxis::stop(double acceleration) {
|
||||
|
||||
// Status of read-write-operations of ASCII commands to the controller
|
||||
asynStatus rwStatus = asynSuccess;
|
||||
|
||||
// Status of parameter library operations
|
||||
asynStatus plStatus = asynSuccess;
|
||||
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
|
||||
// =========================================================================
|
||||
|
||||
rwStatus = pC_->writeRead(axisNo_, "P350=8", response, 0);
|
||||
|
||||
if (rwStatus != asynSuccess) {
|
||||
asynPrint(
|
||||
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
|
||||
"failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
plStatus = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
return asynError;
|
||||
}
|
||||
|
||||
return rwStatus;
|
||||
}
|
||||
|
||||
// The detector tower axis uses absolute encoders
|
||||
asynStatus detectorTowerAngleAxis::readEncoderType() {
|
||||
asynStatus status = setStringParam(pC_->encoderType(), AbsoluteEncoder);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "encoderType_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus
|
||||
detectorTowerAngleAxis::toggleWorkingChangerState(bool toChangingPosition) {
|
||||
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
|
||||
// Status of read-write-operations of ASCII commands to the controller
|
||||
asynStatus rwStatus = asynSuccess;
|
||||
|
||||
// Status of parameter library operations
|
||||
asynStatus plStatus = asynSuccess;
|
||||
|
||||
bool moving = false;
|
||||
int positionState = 0;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
rwStatus = poll(&moving);
|
||||
if (rwStatus != asynSuccess) {
|
||||
return rwStatus;
|
||||
}
|
||||
|
||||
plStatus =
|
||||
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "positionStateRBV_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if (moving) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
||||
"idle and can therefore not be moved to %s state.%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
toChangingPosition ? "changer" : "working",
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
|
||||
plStatus = setStringParam(
|
||||
pC_->motorMessageText(),
|
||||
"Axis cannot be moved to changer position while it is moving.");
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "motorMessageText_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
plStatus = setIntegerParam(pC_->changeStateRBV(), !toChangingPosition);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "changeStateRBV",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Axis is already in the correct position
|
||||
bool isAlreadyThere = (toChangingPosition == false && positionState == 1) ||
|
||||
(toChangingPosition == true && positionState == 2);
|
||||
|
||||
if (isAlreadyThere) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is already "
|
||||
"in %s position.%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
toChangingPosition ? "changer" : "working",
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
|
||||
// Update the PV anyway, even though nothing changed.
|
||||
plStatus = setIntegerParam(pC_->changeStateRBV(), toChangingPosition);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "changeStateRBV",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// Move the axis into changer or working position
|
||||
if (toChangingPosition) {
|
||||
rwStatus = pC_->writeRead(axisNo_, "P350=2", response, 0);
|
||||
} else {
|
||||
rwStatus = pC_->writeRead(axisNo_, "P350=3", response, 0);
|
||||
}
|
||||
|
||||
if (plStatus != asynSuccess) {
|
||||
plStatus = setIntegerParam(pC_->changeStateRBV(), !toChangingPosition);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "changeStateRBV",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
plStatus = setIntegerParam(pC_->changeStateRBV(), toChangingPosition);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "changeStateRBV", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
return rwStatus;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerAngleAxis::adjustOrigin(double newOrigin) {
|
||||
|
||||
asynStatus status = asynSuccess;
|
||||
char command[pC_->MAXBUF_] = {0};
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int positionState = 0;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
status =
|
||||
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "positionStateRBV_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// If the axis is in changer position, it must be moved into working
|
||||
// position before any move can be started.
|
||||
bool isInChangerPos = positionState == 2 || positionState == 3;
|
||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
isInChangerPos, pC_->pasynUser())) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
|
||||
"moved because it is moving from working to changer "
|
||||
"position, is in changer position or is moving from changer "
|
||||
"to working position.%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
}
|
||||
|
||||
// Set the new origin for the angle axis
|
||||
snprintf(command, sizeof(command), "Q556=%lf P350=4", newOrigin);
|
||||
|
||||
// We don't expect an answer
|
||||
status = pC_->writeRead(axisNo_, command, response, 0);
|
||||
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nSetting new "
|
||||
"angle origin %lf failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
newOrigin);
|
||||
status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerAngleAxis::doReset() {
|
||||
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
asynStatus plStatus = asynSuccess;
|
||||
int positionState = 0;
|
||||
|
||||
plStatus =
|
||||
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "positionStateRBV_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
/*
|
||||
Check which action should be performed:
|
||||
- If error_ == 10 or 11 (FTZ motor error): P352 = 3 (Recover FTZ)
|
||||
- If any other error: P352 = 2 (Reset error)
|
||||
- Otherwise: P352 = 1 (Set axis in closed-loop mode)
|
||||
*/
|
||||
if (error_ == 10 || error_ == 11) {
|
||||
return pC_->writeRead(axisNo_, "P352=3", response, 0);
|
||||
} else if (error_ != 0) {
|
||||
return pC_->writeRead(axisNo_, "P352=2", response, 0);
|
||||
} else {
|
||||
return pC_->writeRead(axisNo_, "P352=1", response, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************/
|
||||
/** The following functions are C-wrappers, and can be called directly from
|
||||
* iocsh */
|
||||
|
||||
extern "C" {
|
||||
|
||||
/*
|
||||
C wrapper for the axis constructor. Please refer to the detectorTower
|
||||
constructor documentation. The controller is read from the portName.
|
||||
*/
|
||||
asynStatus detectorTowerCreateAxis(const char *portName, int angleAxisIdx,
|
||||
int liftAxisIdx, int supportAxisIdx) {
|
||||
|
||||
/*
|
||||
findAsynPortDriver is a asyn library FFI function which uses the C ABI.
|
||||
Therefore it returns a void pointer instead of e.g. a pointer to a
|
||||
superclass of the controller such as asynPortDriver. Type-safe upcasting
|
||||
via dynamic_cast is therefore not possible directly. However, we do know
|
||||
that the void pointer is either a pointer to asynPortDriver (if a driver
|
||||
with the specified name exists) or a nullptr. Therefore, we first do a
|
||||
nullptr check, then a cast to asynPortDriver and lastly a (typesafe)
|
||||
dynamic_upcast to Controller
|
||||
https://stackoverflow.com/questions/70906749/is-there-a-safe-way-to-cast-void-to-class-pointer-in-c
|
||||
*/
|
||||
void *ptr = findAsynPortDriver(portName);
|
||||
if (ptr == nullptr) {
|
||||
/*
|
||||
We can't use asynPrint here since this macro would require us
|
||||
to get a lowLevelPortUser_ from a pointer to an asynPortDriver.
|
||||
However, the given pointer is a nullptr and therefore doesn'taxis
|
||||
works w/o that, but doesn't offer the comfort provided
|
||||
by the asynTrace-facility
|
||||
*/
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d\nPort not found.",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
return asynError;
|
||||
}
|
||||
// Unsafe cast of the pointer to an asynPortDriver
|
||||
asynPortDriver *apd = (asynPortDriver *)(ptr);
|
||||
|
||||
// Safe downcast
|
||||
detectorTowerController *pC = dynamic_cast<detectorTowerController *>(apd);
|
||||
if (pC == nullptr) {
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d\nController "
|
||||
"is not a detectorTowerController.",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Assert that the three indices are different from each other
|
||||
if (angleAxisIdx == liftAxisIdx) {
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d\nAll axis indices "
|
||||
"must be unique.",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Prevent manipulation of the controller from other threads while we
|
||||
// create the new axis.
|
||||
pC->lock();
|
||||
|
||||
/*
|
||||
We create a new instance of the axis, using the "new" keyword to
|
||||
allocate it on the heap while avoiding RAII.
|
||||
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
|
||||
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp
|
||||
|
||||
The created object is registered in EPICS in its constructor and can safely
|
||||
be "leaked" here.
|
||||
*/
|
||||
|
||||
detectorTowerAngleAxis *angleAxis =
|
||||
new detectorTowerAngleAxis(pC, angleAxisIdx);
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
detectorTowerLiftAxis *liftAxis =
|
||||
new detectorTowerLiftAxis(pC, liftAxisIdx, angleAxis);
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
detectorTowerSupportAxis *supportAxis =
|
||||
new detectorTowerSupportAxis(pC, supportAxisIdx, angleAxis);
|
||||
|
||||
// Allow manipulation of the controller again
|
||||
pC->unlock();
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mcu1)",
|
||||
iocshArgString};
|
||||
static const iocshArg CreateAxisArg1 = {"Number of the detector angle axis",
|
||||
iocshArgInt};
|
||||
static const iocshArg CreateAxisArg2 = {"Number of the lift axis", iocshArgInt};
|
||||
static const iocshArg CreateAxisArg3 = {"Number of the support axis",
|
||||
iocshArgInt};
|
||||
static const iocshArg *const CreateAxisArgs[] = {
|
||||
&CreateAxisArg0,
|
||||
&CreateAxisArg1,
|
||||
&CreateAxisArg2,
|
||||
&CreateAxisArg3,
|
||||
};
|
||||
static const iocshFuncDef configDetectorTowerCreateAxis = {"detectorTowerAxis",
|
||||
4, CreateAxisArgs};
|
||||
static void configDetectorTowerCreateAxisCallFunc(const iocshArgBuf *args) {
|
||||
detectorTowerCreateAxis(args[0].sval, args[1].ival, args[2].ival,
|
||||
args[3].ival);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @brief Set the setDeferredMovementWait (FFI implementation)
|
||||
*
|
||||
* @param portName Name of the controller
|
||||
* @param axisNo Axis number
|
||||
* @param scaleMovTimeout Scaling factor (in seconds)
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus setDeferredMovementWait(const char *portName, int axisNo,
|
||||
double deferredMovementWait) {
|
||||
|
||||
sinqController *pC;
|
||||
pC = (sinqController *)findAsynPortDriver(portName);
|
||||
if (pC == nullptr) {
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d:\nPort %s not found.",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__, portName);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynMotorAxis *asynAxis = pC->getAxis(axisNo);
|
||||
detectorTowerAngleAxis *axis =
|
||||
dynamic_cast<detectorTowerAngleAxis *>(asynAxis);
|
||||
if (axis == nullptr) {
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d:\nAxis %d does not "
|
||||
"exist or is not an instance of detectorTowerAngleAxis.",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__, axisNo);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
axis->deferredMovementWait_ = deferredMovementWait;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
static const iocshArg setDeferredMovementWaitArg0 = {"Controller port name",
|
||||
iocshArgString};
|
||||
static const iocshArg setDeferredMovementWaitArg1 = {"Axis number",
|
||||
iocshArgInt};
|
||||
static const iocshArg setDeferredMovementWaitArg2 = {
|
||||
"Minimum time a deferred movement will be delayed in order to collect "
|
||||
"commands in seconds",
|
||||
iocshArgDouble};
|
||||
static const iocshArg *const setDeferredMovementWaitArgs[] = {
|
||||
&setDeferredMovementWaitArg0, &setDeferredMovementWaitArg1,
|
||||
&setDeferredMovementWaitArg2};
|
||||
static const iocshFuncDef setDeferredMovementWaitDef = {
|
||||
"setDeferredMovementWait", 3, setDeferredMovementWaitArgs};
|
||||
|
||||
static void setDeferredMovementWaitCallFunc(const iocshArgBuf *args) {
|
||||
setDeferredMovementWait(args[0].sval, args[1].ival, args[2].dval);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
// This function is made known to EPICS in detectorTower.dbd and is
|
||||
// called by EPICS in order to register both functions in the IOC shell
|
||||
static void detectorTowerAxisRegister(void) {
|
||||
iocshRegister(&configDetectorTowerCreateAxis,
|
||||
configDetectorTowerCreateAxisCallFunc);
|
||||
iocshRegister(&setDeferredMovementWaitDef, setDeferredMovementWaitCallFunc);
|
||||
}
|
||||
epicsExportRegistrar(detectorTowerAxisRegister);
|
||||
|
||||
} // extern "C"
|
@ -1,34 +1,31 @@
|
||||
#ifndef detectorTowerAxis_H
|
||||
#define detectorTowerAxis_H
|
||||
#include "auxiliaryAxis.h"
|
||||
#ifndef detectorTowerAngleAxis_H
|
||||
#define detectorTowerAngleAxis_H
|
||||
#include "turboPmacAxis.h"
|
||||
#include <errlog.h>
|
||||
|
||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||
// between the controller and the axis .h-file. See
|
||||
// https://en.cppreference.com/w/cpp/language/class.
|
||||
class detectorTowerController;
|
||||
class detectorTowerLiftAxis;
|
||||
class detectorTowerSupportAxis;
|
||||
|
||||
class detectorTowerAxis : public turboPmacAxis {
|
||||
class detectorTowerAngleAxis : public turboPmacAxis {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new detectorTowerAxis
|
||||
* @brief Construct a new detectorTowerAngleAxis
|
||||
*
|
||||
* @param pController Pointer to the associated controller
|
||||
* @param axisNo Index of the axis
|
||||
* @param liftOffsetAxis Pointer to the attached axis which controls
|
||||
* the lift offset
|
||||
* @param tiltOffsetAxis Pointer to the attached axis which controls
|
||||
* the tilt offset
|
||||
* @param liftAxis Pointer to the associated lift axis
|
||||
*/
|
||||
detectorTowerAxis(detectorTowerController *pController, int axisNo,
|
||||
auxiliaryAxis *liftOffsetAxis,
|
||||
auxiliaryAxis *tiltOffsetAxis);
|
||||
detectorTowerAngleAxis(detectorTowerController *pController, int axisNo);
|
||||
|
||||
/**
|
||||
* @brief Destroy the detectorTowerAxis
|
||||
* @brief Destroy the detectorTowerAngleAxis
|
||||
*
|
||||
*/
|
||||
virtual ~detectorTowerAxis();
|
||||
virtual ~detectorTowerAngleAxis();
|
||||
|
||||
/**
|
||||
* @brief Readout of some values from the controller at IOC startup
|
||||
@ -47,12 +44,17 @@ class detectorTowerAxis : public turboPmacAxis {
|
||||
asynStatus stop(double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `doPoll` function from sinqAxis. The
|
||||
* parameters are described in the documentation of `sinqAxis::doPoll`.
|
||||
* @brief Poll all detector tower axes, if this axis is the one with the
|
||||
* smallest index.
|
||||
*
|
||||
* We do not use the doPoll framework from sinqMotor here on purpose, since
|
||||
* we want to e.g. reset the motorStatusProblem for all axes at once at the
|
||||
* beginning of the poll.
|
||||
*
|
||||
* @param moving
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus doPoll(bool *moving);
|
||||
|
||||
/**
|
||||
@ -92,16 +94,57 @@ class detectorTowerAxis : public turboPmacAxis {
|
||||
*/
|
||||
asynStatus toggleWorkingChangerState(bool toChangingPosition);
|
||||
|
||||
/**
|
||||
* @brief Overwrite the `reset` function so it doesn't do fast polls
|
||||
* afterwards.
|
||||
*
|
||||
* The
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus reset() { return doReset(); }
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `doReset` method of sinqAxis
|
||||
*
|
||||
* @param on
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doReset();
|
||||
|
||||
// If true, either this axis or one of the auxiliaryAxis attached to it
|
||||
// received a movement command.
|
||||
/**
|
||||
* @brief Move the axis to the position `newOrigin` and recalibrate
|
||||
*
|
||||
* When calling this function, the angle axis moves to `newOrigin` and the
|
||||
* hardware sets this position as the new origin.
|
||||
*
|
||||
* @param origin
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus adjustOrigin(double newOrigin);
|
||||
|
||||
/**
|
||||
* @brief Read out the beam radius
|
||||
*
|
||||
* @return double
|
||||
*/
|
||||
double beamRadius() { return beamRadius_; }
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the associated angle axis
|
||||
*
|
||||
* @return detectorTowerAngleAxis*
|
||||
*/
|
||||
detectorTowerLiftAxis *liftAxis() { return liftAxis_; }
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the associated support axis
|
||||
*
|
||||
* @return detectorTowerAngleAxis*
|
||||
*/
|
||||
detectorTowerSupportAxis *supportAxis() { return supportAxis_; }
|
||||
|
||||
// If true, either this axis or one of the detectorTowerLiftAxis
|
||||
// attached to it received a movement command.
|
||||
bool receivedTarget_;
|
||||
|
||||
// If set to true, the virtual axis is about to start a deferred movement
|
||||
@ -115,14 +158,21 @@ class detectorTowerAxis : public turboPmacAxis {
|
||||
*/
|
||||
double deferredMovementWait_;
|
||||
|
||||
/**
|
||||
* @brief Variable holding the axis error for later use
|
||||
*
|
||||
*/
|
||||
int error_;
|
||||
|
||||
protected:
|
||||
detectorTowerController *pC_;
|
||||
int error_;
|
||||
auxiliaryAxis *tiltOffsetAxis_;
|
||||
auxiliaryAxis *liftOffsetAxis_;
|
||||
detectorTowerLiftAxis *liftAxis_;
|
||||
detectorTowerSupportAxis *supportAxis_;
|
||||
double beamRadius_;
|
||||
|
||||
private:
|
||||
friend class auxiliaryAxis;
|
||||
friend class detectorTowerLiftAxis;
|
||||
friend class detectorTowerSupportAxis;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -8,8 +8,9 @@
|
||||
|
||||
#ifndef detectorTowerController_H
|
||||
#define detectorTowerController_H
|
||||
#include "auxiliaryAxis.h"
|
||||
#include "detectorTowerAxis.h"
|
||||
#include "detectorTowerAngleAxis.h"
|
||||
#include "detectorTowerLiftAxis.h"
|
||||
#include "detectorTowerSupportAxis.h"
|
||||
#include "turboPmacController.h"
|
||||
|
||||
class detectorTowerController : public turboPmacController {
|
||||
@ -19,7 +20,7 @@ class detectorTowerController : public turboPmacController {
|
||||
* @brief Construct a new detectorTowerController object
|
||||
*
|
||||
* This controller object can handle both "normal" TurboPMAC axes created
|
||||
with the turboPmacAxis constructor and the special detectorTowerAxis.
|
||||
with the turboPmacAxis constructor and the special detectorTowerAngleAxis.
|
||||
*
|
||||
* @param portName See sinqController constructor
|
||||
* @param ipPortConfigName See sinqController constructor
|
||||
@ -52,34 +53,105 @@ class detectorTowerController : public turboPmacController {
|
||||
*/
|
||||
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
|
||||
|
||||
/**
|
||||
* @brief Overloaded function of turboPmacController
|
||||
*
|
||||
* @param pasynUser Specify the axis via the asynUser
|
||||
* @param value New value
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
|
||||
|
||||
/**
|
||||
* @brief Get the axis object
|
||||
*
|
||||
* @param pasynUser Specify the axis via the asynUser
|
||||
* @return detectorTowerAxis* If no axis could be found, this is a
|
||||
* nullptr
|
||||
* @return detectorTowerAngleAxis* If no axis could be found, this is
|
||||
* a nullptr
|
||||
*/
|
||||
detectorTowerAxis *getDetectorTowerAxis(asynUser *pasynUser);
|
||||
detectorTowerAngleAxis *getDetectorTowerAngleAxis(asynUser *pasynUser);
|
||||
|
||||
/**
|
||||
* @brief Get the axis object
|
||||
*
|
||||
* @param axisNo Specify the axis via its index
|
||||
* @return detectorTowerAxis* If no axis could be found, this is a
|
||||
* nullptr
|
||||
* @return detectorTowerAngleAxis* If no axis could be found, this is
|
||||
* a nullptr
|
||||
*/
|
||||
detectorTowerAxis *getDetectorTowerAxis(int axisNo);
|
||||
detectorTowerAngleAxis *getDetectorTowerAngleAxis(int axisNo);
|
||||
|
||||
/**
|
||||
* @brief Get the axis object
|
||||
*
|
||||
* @param pasynUser Specify the axis via the asynUser
|
||||
* @return detectorTowerLiftAxis* If no axis could be found,
|
||||
* this is a nullptr
|
||||
*/
|
||||
detectorTowerLiftAxis *getDetectorTowerLiftAxis(asynUser *pasynUser);
|
||||
|
||||
/**
|
||||
* @brief Get the axis object
|
||||
*
|
||||
* @param axisNo Specify the axis via its index
|
||||
* @return detectorTowerLiftAxis* If no axis could be found,
|
||||
* this is a nullptr
|
||||
*/
|
||||
detectorTowerLiftAxis *getDetectorTowerLiftAxis(int axisNo);
|
||||
|
||||
/**
|
||||
* @brief Get the axis object
|
||||
*
|
||||
* @param pasynUser Specify the axis via the asynUser
|
||||
* @return detectorTowerSupportAxis* If no axis could be found,
|
||||
* this is a nullptr
|
||||
*/
|
||||
detectorTowerSupportAxis *getDetectorTowerSupportAxis(asynUser *pasynUser);
|
||||
|
||||
/**
|
||||
* @brief Get the axis object
|
||||
*
|
||||
* @param axisNo Specify the axis via its index
|
||||
* @return detectorTowerSupportAxis* If no axis could be found,
|
||||
* this is a nullptr
|
||||
*/
|
||||
detectorTowerSupportAxis *getDetectorTowerSupportAxis(int axisNo);
|
||||
|
||||
/**
|
||||
* @brief Common poll function for both lift and angle axes
|
||||
*
|
||||
* @param moving
|
||||
* @param angleAxis
|
||||
* @param liftAxis
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus pollDetectorAxes(bool *moving, detectorTowerAngleAxis *angleAxis,
|
||||
detectorTowerLiftAxis *liftAxis,
|
||||
detectorTowerSupportAxis *supportAxis);
|
||||
|
||||
// Accessors for additional PVs
|
||||
int positionStateRBV() { return positionStateRBV_; }
|
||||
int changeState() { return changeState_; }
|
||||
int changeStateRBV() { return changeStateRBV_; }
|
||||
int motorOrigin() { return motorOrigin_; }
|
||||
int motorAdjustOrigin() { return motorAdjustOrigin_; }
|
||||
int motorAdjustOriginHighLimitFromDriver() {
|
||||
return motorAdjustOriginHighLimitFromDriver_;
|
||||
}
|
||||
int motorAdjustOriginLowLimitFromDriver() {
|
||||
return motorAdjustOriginLowLimitFromDriver_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Indices of additional PVs
|
||||
#define FIRST_detectorTower_PARAM positionStateRBV_
|
||||
int positionStateRBV_;
|
||||
int changeState_;
|
||||
#define LAST_detectorTower_PARAM changeState_
|
||||
int changeStateRBV_;
|
||||
int motorOrigin_;
|
||||
int motorAdjustOrigin_;
|
||||
int motorAdjustOriginHighLimitFromDriver_;
|
||||
int motorAdjustOriginLowLimitFromDriver_;
|
||||
#define LAST_detectorTower_PARAM motorAdjustOriginLowLimitFromDriver_
|
||||
};
|
||||
#define NUM_detectorTower_DRIVER_PARAMS \
|
||||
(&LAST_detectorTower_PARAM - &FIRST_detectorTower_PARAM + 1)
|
||||
|
283
src/detectorTowerLiftAxis.cpp
Normal file
283
src/detectorTowerLiftAxis.cpp
Normal file
@ -0,0 +1,283 @@
|
||||
#include "detectorTowerLiftAxis.h"
|
||||
#include "detectorTowerAngleAxis.h"
|
||||
#include "detectorTowerController.h"
|
||||
#include "turboPmacController.h"
|
||||
#include <epicsExport.h>
|
||||
#include <errlog.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
/*
|
||||
Contains all instances of detectorTowerLiftAxis which have been created and
|
||||
is used in the initialization hook function.
|
||||
*/
|
||||
static std::vector<detectorTowerLiftAxis *> axes;
|
||||
|
||||
/**
|
||||
* @brief Hook function to perform certain actions during the IOC initialization
|
||||
*
|
||||
* @param iState
|
||||
*/
|
||||
static void epicsInithookFunction(initHookState iState) {
|
||||
if (iState == initHookAfterDatabaseRunning) {
|
||||
// Iterate through all axes of each and call the initialization method
|
||||
// on each one of them.
|
||||
for (std::vector<detectorTowerLiftAxis *>::iterator itA = axes.begin();
|
||||
itA != axes.end(); ++itA) {
|
||||
detectorTowerLiftAxis *axis = *itA;
|
||||
axis->init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
detectorTowerLiftAxis::detectorTowerLiftAxis(detectorTowerController *pC,
|
||||
int axisNo,
|
||||
detectorTowerAngleAxis *angleAxis)
|
||||
: turboPmacAxis(pC, axisNo, false), pC_(pC) {
|
||||
|
||||
/*
|
||||
The superclass constructor sinqAxis calls in turn its superclass
|
||||
constructor asynMotorAxis. In the latter, a pointer to the constructed
|
||||
object this is stored inside the array pAxes_:
|
||||
|
||||
pC->pAxes_[axisNo] = this;
|
||||
|
||||
Therefore, the axes are managed by the controller pC. If axisNo is out
|
||||
of bounds, asynMotorAxis prints an error (see
|
||||
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()) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d: FATAL ERROR: "
|
||||
"Axis index %d must be smaller than the total number of axes "
|
||||
"%d",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
axisNo_, pC->numAxes());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Register the hook function during construction of the first axis
|
||||
// object
|
||||
if (axes.empty()) {
|
||||
initHookRegister(&epicsInithookFunction);
|
||||
}
|
||||
|
||||
// Collect all axes into this list which will be used in the hook
|
||||
// function
|
||||
axes.push_back(this);
|
||||
|
||||
angleAxis_ = angleAxis;
|
||||
angleAxis->liftAxis_ = this;
|
||||
}
|
||||
|
||||
detectorTowerLiftAxis::~detectorTowerLiftAxis(void) {
|
||||
// Since the controller memory is managed somewhere else, we don't need
|
||||
// to clean up the pointer pC here.
|
||||
}
|
||||
|
||||
asynStatus detectorTowerLiftAxis::init() {
|
||||
|
||||
// Local variable declaration
|
||||
asynStatus status = asynSuccess;
|
||||
double motorRecResolution = 0.0;
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int positionState = 0;
|
||||
int nvals = 0;
|
||||
|
||||
// The parameter library takes some time to be initialized. Therefore we
|
||||
// wait until the status is not asynParamUndefined anymore.
|
||||
time_t now = time(NULL);
|
||||
time_t maxInitTime = 60;
|
||||
while (1) {
|
||||
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||
&motorRecResolution);
|
||||
if (status == asynParamUndefined) {
|
||||
if (now + maxInitTime < time(NULL)) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d\nInitializing the parameter library failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
return asynError;
|
||||
}
|
||||
} else if (status == asynSuccess) {
|
||||
break;
|
||||
} else if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorRecResolution_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the motorStatusMoving flag
|
||||
status = setIntegerParam(pC_->motorStatusMoving(), 0);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusMoving_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// Check if we are currently in the changer position and update the PV
|
||||
// accordingly
|
||||
status = pC_->writeRead(axisNo_, "P358", response, 1);
|
||||
nvals = sscanf(response, "%d", &positionState);
|
||||
if (nvals != 1) {
|
||||
return pC_->couldNotParseResponse("P358", response, axisNo(),
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
status = setIntegerParam(pC_->changeStateRBV(), positionState == 2);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "changeStateRBV", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
return callParamCallbacks();
|
||||
}
|
||||
|
||||
// Perform the actual poll
|
||||
asynStatus detectorTowerLiftAxis::poll(bool *moving) {
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Is this axis the one with the smallest index?
|
||||
// If not, just read out the movement status and update *moving
|
||||
if (axisNo() < angleAxis()->axisNo() &&
|
||||
axisNo() < angleAxis()->supportAxis()->axisNo()) {
|
||||
status = pC_->pollDetectorAxes(moving, angleAxis(), this,
|
||||
angleAxis()->supportAxis());
|
||||
} else {
|
||||
status = pC_->getIntegerParam(axisNo(), pC_->motorStatusMoving(),
|
||||
(int *)moving);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusMoving",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
wasMoving_ = *moving;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerLiftAxis::doPoll(bool *moving) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nThe doPoll method "
|
||||
"of this axis type should not be reachable. This is a bug.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerLiftAxis::doMove(double position, int relative,
|
||||
double min_velocity,
|
||||
double max_velocity,
|
||||
double acceleration) {
|
||||
|
||||
double motorRecResolution = 0.0;
|
||||
asynStatus plStatus = pC_->getDoubleParam(
|
||||
axisNo_, pC_->motorRecResolution(), &motorRecResolution);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "motorRecResolution_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
// Signal to the deferredMovementCollectorLoop (of the
|
||||
// detectorTowerAngleAxis) that a movement should be started to the
|
||||
// defined target position.
|
||||
targetPosition_ = position * motorRecResolution;
|
||||
angleAxis_->receivedTarget_ = true;
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerLiftAxis::reset() { return angleAxis()->reset(); };
|
||||
|
||||
asynStatus detectorTowerLiftAxis::stop(double acceleration) {
|
||||
|
||||
// Status of read-write-operations of ASCII commands to the controller
|
||||
asynStatus rwStatus = asynSuccess;
|
||||
|
||||
// Status of parameter library operations
|
||||
asynStatus plStatus = asynSuccess;
|
||||
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
|
||||
// =========================================================================
|
||||
|
||||
rwStatus = pC_->writeRead(axisNo_, "P350=8", response, 0);
|
||||
|
||||
if (rwStatus != asynSuccess) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nStopping "
|
||||
"the movement "
|
||||
"failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
plStatus = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (plStatus != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(plStatus, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
return asynError;
|
||||
}
|
||||
|
||||
return rwStatus;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerLiftAxis::readEncoderType() {
|
||||
return angleAxis()->readEncoderType();
|
||||
}
|
||||
|
||||
asynStatus detectorTowerLiftAxis::adjustOrigin(double newOrigin) {
|
||||
|
||||
asynStatus status = asynSuccess;
|
||||
char command[pC_->MAXBUF_] = {0};
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int positionState = 0;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
status =
|
||||
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "positionStateRBV_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// If the axis is in changer position, it must be moved into working
|
||||
// position before any move can be started.
|
||||
bool isInChangerPos = positionState == 2 || positionState == 3;
|
||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
isInChangerPos, pC_->pasynUser())) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
|
||||
"moved because it is moving from working to changer "
|
||||
"position, is in changer position or is moving from changer "
|
||||
"to working position.%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
}
|
||||
|
||||
// Set the new adjust for the lift axis
|
||||
snprintf(command, sizeof(command), "Q356=%lf P350=5", newOrigin);
|
||||
|
||||
// We don't expect an answer
|
||||
status = pC_->writeRead(axisNo_, command, response, 0);
|
||||
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nSetting new "
|
||||
"lift origin %lf failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
newOrigin);
|
||||
status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
125
src/detectorTowerLiftAxis.h
Normal file
125
src/detectorTowerLiftAxis.h
Normal file
@ -0,0 +1,125 @@
|
||||
#ifndef detectorTowerLiftAxis_H
|
||||
#define detectorTowerLiftAxis_H
|
||||
#include "turboPmacAxis.h"
|
||||
|
||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||
// between the controller and the axis .h-file. See
|
||||
// https://en.cppreference.com/w/cpp/language/class.
|
||||
class detectorTowerController;
|
||||
class detectorTowerAngleAxis;
|
||||
|
||||
class detectorTowerLiftAxis : public turboPmacAxis {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new detectorTowerAngleAxis
|
||||
*
|
||||
* @param pController Pointer to the associated controller
|
||||
* @param axisNo Index of the axis
|
||||
*/
|
||||
detectorTowerLiftAxis(detectorTowerController *pController, int axisNo,
|
||||
detectorTowerAngleAxis *angleAxis);
|
||||
|
||||
/**
|
||||
* @brief Destroy the turboPmacAxis
|
||||
*
|
||||
*/
|
||||
virtual ~detectorTowerLiftAxis();
|
||||
|
||||
/**
|
||||
* @brief Readout of some values from the controller at IOC startup
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus init();
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `stop` function from asynMotorAxis
|
||||
*
|
||||
* @param acceleration Acceleration ACCEL from the motor record. This
|
||||
* value is currently not used.
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus stop(double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Poll all detector tower axes, if this axis is the one with the
|
||||
* smallest index.
|
||||
*
|
||||
* We do not use the doPoll framework from sinqMotor here on purpose, since
|
||||
* we want to e.g. reset the motorStatusProblem for all axes at once at the
|
||||
* beginning of the poll.
|
||||
*
|
||||
* @param moving
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus doPoll(bool *moving);
|
||||
|
||||
/**
|
||||
* @brief Set the target value for the detector lift and trigger a position
|
||||
* collection cycle
|
||||
*
|
||||
* @param position
|
||||
* @param relative
|
||||
* @param min_velocity
|
||||
* @param max_velocity
|
||||
* @param acceleration
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doMove(double position, int relative, double min_velocity,
|
||||
double max_velocity, double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Calls the `reset` function of the associated angle axis.
|
||||
*
|
||||
* @param on
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus reset();
|
||||
|
||||
/**
|
||||
* @brief Move the axis to the position `newOrigin` and recalibrate
|
||||
*
|
||||
* When calling this function, the lift axis moves to `newOrigin` and the
|
||||
* hardware sets this position as the new origin.
|
||||
*
|
||||
* @param origin
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus adjustOrigin(double newOrigin);
|
||||
|
||||
/**
|
||||
* @brief Move the support motor of the beam lift to the position
|
||||
* `newOrigin` and recalibrate
|
||||
*
|
||||
* When calling this function, the lift axis support motor moves to
|
||||
* `newOrigin` and the hardware sets this position as the new origin.
|
||||
*
|
||||
* @param origin
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus adjustSupportOrigin(double newOrigin);
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the associated angle axis
|
||||
*
|
||||
* @return detectorTowerAngleAxis*
|
||||
*/
|
||||
detectorTowerAngleAxis *angleAxis() { return angleAxis_; }
|
||||
|
||||
/**
|
||||
* @brief This axis type has an absolute encoder by default
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus readEncoderType();
|
||||
|
||||
protected:
|
||||
detectorTowerController *pC_;
|
||||
detectorTowerAngleAxis *angleAxis_;
|
||||
|
||||
private:
|
||||
friend class detectorTowerAngleAxis;
|
||||
};
|
||||
|
||||
#endif
|
232
src/detectorTowerSupportAxis.cpp
Normal file
232
src/detectorTowerSupportAxis.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
#include "detectorTowerSupportAxis.h"
|
||||
#include "detectorTowerAngleAxis.h"
|
||||
#include "detectorTowerController.h"
|
||||
#include "turboPmacController.h"
|
||||
#include <epicsExport.h>
|
||||
#include <errlog.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
/*
|
||||
Contains all instances of detectorTowerSupportAxis which have been created and
|
||||
is used in the initialization hook function.
|
||||
*/
|
||||
static std::vector<detectorTowerSupportAxis *> axes;
|
||||
|
||||
/**
|
||||
* @brief Hook function to perform certain actions during the IOC initialization
|
||||
*
|
||||
* @param iState
|
||||
*/
|
||||
static void epicsInithookFunction(initHookState iState) {
|
||||
if (iState == initHookAfterDatabaseRunning) {
|
||||
// Iterate through all axes of each and call the initialization method
|
||||
// on each one of them.
|
||||
for (std::vector<detectorTowerSupportAxis *>::iterator itA =
|
||||
axes.begin();
|
||||
itA != axes.end(); ++itA) {
|
||||
detectorTowerSupportAxis *axis = *itA;
|
||||
axis->init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
detectorTowerSupportAxis::detectorTowerSupportAxis(
|
||||
detectorTowerController *pC, int axisNo, detectorTowerAngleAxis *angleAxis)
|
||||
: turboPmacAxis(pC, axisNo, false), pC_(pC) {
|
||||
|
||||
/*
|
||||
The superclass constructor sinqAxis calls in turn its superclass constructor
|
||||
asynMotorAxis. In the latter, a pointer to the constructed object this is
|
||||
stored inside the array pAxes_:
|
||||
|
||||
pC->pAxes_[axisNo] = this;
|
||||
|
||||
Therefore, the axes are managed by the controller pC. If axisNo is out of
|
||||
bounds, asynMotorAxis prints an error (see
|
||||
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()) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d: FATAL ERROR: "
|
||||
"Axis index %d must be smaller than the total number of axes "
|
||||
"%d",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
axisNo_, pC->numAxes());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Register the hook function during construction of the first axis object
|
||||
if (axes.empty()) {
|
||||
initHookRegister(&epicsInithookFunction);
|
||||
}
|
||||
|
||||
// Collect all axes into this list which will be used in the hook
|
||||
// function
|
||||
axes.push_back(this);
|
||||
|
||||
// Link the axes to each other
|
||||
angleAxis_ = angleAxis;
|
||||
angleAxis_->supportAxis_ = this;
|
||||
}
|
||||
|
||||
detectorTowerSupportAxis::~detectorTowerSupportAxis(void) {
|
||||
// Since the controller memory is managed somewhere else, we don't need to
|
||||
// clean up the pointer pC here.
|
||||
}
|
||||
|
||||
asynStatus detectorTowerSupportAxis::init() {
|
||||
|
||||
// Local variable declaration
|
||||
asynStatus status = asynSuccess;
|
||||
double motorRecResolution = 0.0;
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int positionState = 0;
|
||||
int nvals = 0;
|
||||
|
||||
// The parameter library takes some time to be initialized. Therefore we
|
||||
// wait until the status is not asynParamUndefined anymore.
|
||||
time_t now = time(NULL);
|
||||
time_t maxInitTime = 60;
|
||||
while (1) {
|
||||
status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
|
||||
&motorRecResolution);
|
||||
if (status == asynParamUndefined) {
|
||||
if (now + maxInitTime < time(NULL)) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d\nInitializing the parameter library failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
return asynError;
|
||||
}
|
||||
} else if (status == asynSuccess) {
|
||||
break;
|
||||
} else if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorRecResolution_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the motorStatusMoving flag
|
||||
status = setIntegerParam(pC_->motorStatusMoving(), 0);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusMoving_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// Check if we are currently in the changer position and update the PV
|
||||
// accordingly
|
||||
status = pC_->writeRead(axisNo_, "P358", response, 1);
|
||||
nvals = sscanf(response, "%d", &positionState);
|
||||
if (nvals != 1) {
|
||||
return pC_->couldNotParseResponse("P358", response, axisNo(),
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
status = setIntegerParam(pC_->changeStateRBV(), positionState == 2);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "changeStateRBV", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
return callParamCallbacks();
|
||||
}
|
||||
|
||||
// Perform the actual poll
|
||||
asynStatus detectorTowerSupportAxis::poll(bool *moving) {
|
||||
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Is this axis the one with the smallest index?
|
||||
// If not, just read out the movement status and update *moving
|
||||
if (axisNo() < angleAxis()->liftAxis()->axisNo() &&
|
||||
axisNo() < angleAxis()->axisNo()) {
|
||||
status = pC_->pollDetectorAxes(moving, angleAxis(),
|
||||
angleAxis()->liftAxis(), this);
|
||||
} else {
|
||||
status = pC_->getIntegerParam(axisNo(), pC_->motorStatusMoving(),
|
||||
(int *)moving);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusMoving",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
wasMoving_ = *moving;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerSupportAxis::doPoll(bool *moving) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nThe doPoll method "
|
||||
"of this axis type should not be reachable. This is a bug.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynStatus detectorTowerSupportAxis::stop(double acceleration) {
|
||||
return angleAxis()->stop(acceleration);
|
||||
}
|
||||
|
||||
asynStatus detectorTowerSupportAxis::reset() { return angleAxis()->reset(); };
|
||||
|
||||
asynStatus detectorTowerSupportAxis::readEncoderType() {
|
||||
return angleAxis()->readEncoderType();
|
||||
}
|
||||
|
||||
asynStatus detectorTowerSupportAxis::adjustOrigin(double newOrigin) {
|
||||
|
||||
asynStatus status = asynSuccess;
|
||||
char command[pC_->MAXBUF_] = {0};
|
||||
char response[pC_->MAXBUF_] = {0};
|
||||
int positionState = 0;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
status =
|
||||
pC_->getIntegerParam(axisNo_, pC_->positionStateRBV(), &positionState);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "positionStateRBV_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// If the axis is in changer position, it must be moved into working
|
||||
// position before any move can be started.
|
||||
bool isInChangerPos = positionState == 2 || positionState == 3;
|
||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
isInChangerPos, pC_->pasynUser())) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis cannot be "
|
||||
"moved because it is moving from working to changer "
|
||||
"position, is in changer position or is moving from changer "
|
||||
"to working position.%s\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
pC_->getMsgPrintControl().getSuffix());
|
||||
}
|
||||
|
||||
// Set the new adjust for the lift axis
|
||||
snprintf(command, sizeof(command), "Q656=%lf P350=6", newOrigin);
|
||||
|
||||
// We don't expect an answer
|
||||
status = pC_->writeRead(axisNo_, command, response, 0);
|
||||
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nSetting new "
|
||||
"lift origin %lf failed.\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
newOrigin);
|
||||
status = setIntegerParam(pC_->motorStatusProblem(), true);
|
||||
if (status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(status, "motorStatusProblem_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
127
src/detectorTowerSupportAxis.h
Normal file
127
src/detectorTowerSupportAxis.h
Normal file
@ -0,0 +1,127 @@
|
||||
#ifndef detectorTowerSupportAxis_H
|
||||
#define detectorTowerSupportAxis_H
|
||||
#include "turboPmacAxis.h"
|
||||
|
||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||
// between the controller and the axis .h-file. See
|
||||
// https://en.cppreference.com/w/cpp/language/class.
|
||||
class detectorTowerController;
|
||||
class detectorTowerAngleAxis;
|
||||
|
||||
/**
|
||||
* @brief Passive axis which is mostly controlled indirectly by the hardware
|
||||
*
|
||||
*/
|
||||
class detectorTowerSupportAxis : public turboPmacAxis {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new detectorTowerSupportAxis
|
||||
*
|
||||
* @param pController Pointer to the associated controller
|
||||
* @param axisNo Index of the axis
|
||||
* @param liftAxis Pointer to the associated lift axis
|
||||
*/
|
||||
detectorTowerSupportAxis(detectorTowerController *pController, int axisNo,
|
||||
detectorTowerAngleAxis *angleAxis);
|
||||
|
||||
/**
|
||||
* @brief Destroy the detectorTowerSupportAxis
|
||||
*
|
||||
*/
|
||||
virtual ~detectorTowerSupportAxis();
|
||||
|
||||
/**
|
||||
* @brief Readout of some values from the controller at IOC startup
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus init();
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `stop` function from asynMotorAxis
|
||||
*
|
||||
* @param acceleration Acceleration ACCEL from the motor record. This
|
||||
* value is currently not used.
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus stop(double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Poll all detector tower axes, if this axis is the one with the
|
||||
* smallest index.
|
||||
*
|
||||
* We do not use the doPoll framework from sinqMotor here on purpose, since
|
||||
* we want to e.g. reset the motorStatusProblem for all axes at once at the
|
||||
* beginning of the poll.
|
||||
*
|
||||
* @param moving
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus doPoll(bool *moving);
|
||||
|
||||
/**
|
||||
* @brief This axis cannot be moved directly
|
||||
*
|
||||
* @param position
|
||||
* @param relative
|
||||
* @param min_velocity
|
||||
* @param max_velocity
|
||||
* @param acceleration
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doMove(double position, int relative, double min_velocity,
|
||||
double max_velocity, double acceleration) {
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If the input value is true, move the axis into working position,
|
||||
* otherwise into changer position.
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus toggleWorkingChangerState(bool toChangingPosition);
|
||||
|
||||
/**
|
||||
* @brief Calls the `reset` function of the associated lift axis.
|
||||
*
|
||||
* @param on
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus reset();
|
||||
|
||||
/**
|
||||
* @brief Move the axis to the position `newOrigin` and recalibrate
|
||||
*
|
||||
* When calling this function, the angle axis moves to `newOrigin` and the
|
||||
* hardware sets this position as the new origin.
|
||||
*
|
||||
* @param origin
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus adjustOrigin(double newOrigin);
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the associated angle axis
|
||||
*
|
||||
* @return detectorTowerAngleAxis*
|
||||
*/
|
||||
detectorTowerAngleAxis *angleAxis() { return angleAxis_; }
|
||||
|
||||
/**
|
||||
* @brief This axis type has an absolute encoder by default
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus readEncoderType();
|
||||
|
||||
protected:
|
||||
detectorTowerController *pC_;
|
||||
detectorTowerAngleAxis *angleAxis_;
|
||||
|
||||
private:
|
||||
friend class detectorTowerLiftAxis;
|
||||
};
|
||||
|
||||
#endif
|
1
turboPmac
Submodule
1
turboPmac
Submodule
Submodule turboPmac added at 55b523ddaa
Reference in New Issue
Block a user