Compare commits
86 Commits
amorsim
...
SINQSW-107
Author | SHA1 | Date | |
---|---|---|---|
848da9dfe3 | |||
af61358351 | |||
39098fd0d1 | |||
1539bfc66a | |||
d88e5877a7 | |||
118e177e04 | |||
f61daf0b49 | |||
6dbd0fc0d7 | |||
e657ea675c | |||
5c0c917be9 | |||
35c12274b0 | |||
a6a8f14b26 | |||
33f118ce1e | |||
adf8b30692 | |||
c972cce072 | |||
64c8b08ce4 | |||
30228adf50 | |||
d9d6dae19f | |||
32a8c27dbf | |||
235e403fb3 | |||
b4e201ae86 | |||
9422353107 | |||
e3ac2962f5 | |||
7a81e2c5a0 | |||
f1a17bc295 | |||
8a6441927a | |||
72afd02b4e | |||
f333a27482 | |||
4a2731b054 | |||
9055a86b57 | |||
ccd73babd5 | |||
b8896b7a85 | |||
acf1751081 | |||
61290b5e2c | |||
d706915a46 | |||
3c9932dc18 | |||
9b9072b83b | |||
3b2a21094c | |||
a899a28182 | |||
0cbfe45893 | |||
bb1c10c7cd | |||
339da2e9ff | |||
80877aa6ab | |||
afc92bde3f | |||
8e2c1af10e | |||
f5da0d54bd | |||
ad05433602 | |||
92364a1de8 | |||
403eecafae | |||
fbf2331a05 | |||
14bbda3364 | |||
c7fea08718 | |||
edc71af235 | |||
c805385ad1 | |||
929f9f600d | |||
8c3e68394f | |||
646607e476 | |||
68a265b199 | |||
542abcbaad | |||
efbd0e19cf | |||
6de4a878ef | |||
d86da602d2 | |||
c9e7830274 | |||
d1d74f4db3 | |||
d1beb9b0d8 | |||
3ae13875cf | |||
f56ef2c74c | |||
a25f8cabb9 | |||
c4fe45c0cb | |||
3b7133ecfe | |||
e9a615d0fa | |||
12249d5471 | |||
6d823e2265 | |||
75de7a3a4c | |||
5aefbd4684 | |||
3133d933fa | |||
bf2ff63a4b | |||
b4b093efdf | |||
f424477a6a | |||
3bea34700e | |||
02ab5ff9b8 | |||
972131d86a | |||
f909c41a2b | |||
460030e410 | |||
2a964503a4 | |||
1687ebf4c7 |
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
|
||||
...
|
||||
|
125
.gitignore
vendored
Normal file
125
.gitignore
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
# Took these from the https://github.com/github/gitignore project on October 21, 2011
|
||||
|
||||
# **** 'Personal' entries don't belong in here - put them in your .git/info/exclude file ****
|
||||
|
||||
# Ignore text editor (e.g. emacs) autosave files
|
||||
*~
|
||||
|
||||
# Build Artifacts
|
||||
O.*_Common/
|
||||
O.*_RHEL8-x86_64/
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
*.d
|
||||
SICServer*
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
|
||||
# Compiled python files
|
||||
*.py[co]
|
||||
|
||||
# Eclipse-generated files
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/**
|
||||
tmp/**
|
||||
tmp/**/*
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
*.sln
|
||||
*.vcproj
|
||||
*.exe
|
||||
*.vcxproj
|
||||
*.filters
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
*.sdf
|
||||
.cvsignore
|
||||
|
||||
#Test results
|
||||
*.log
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.vspscc
|
||||
.builds
|
||||
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*
|
||||
|
||||
|
||||
|
||||
# Others
|
||||
*.autosave
|
||||
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
|
||||
|
||||
# Mac OS X Finder
|
||||
.DS_Store
|
||||
._*
|
1
10-llbDevices.rules
Normal file
1
10-llbDevices.rules
Normal file
@ -0,0 +1 @@
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="04b4", ATTR{idProduct}=="1002", MODE="0666" GROUP="plugdev", TAG+="uaccess"
|
22
MakeAutoSave.RHEL7
Normal file
22
MakeAutoSave.RHEL7
Normal file
@ -0,0 +1,22 @@
|
||||
# This Makefile has to be executed in the synApps-xxx/support/autosave-xxx directory.
|
||||
# It creates a autosave module to load into iocsh
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=autosave
|
||||
|
||||
BUILDCLASSES=Linux
|
||||
|
||||
USR_DEPENDENCIES=
|
||||
|
||||
SOURCES += asApp/src/dbrestore.c
|
||||
SOURCES += asApp/src/save_restore.c
|
||||
SOURCES += asApp/src/initHooks.c
|
||||
SOURCES += asApp/src/fGetDateStr.c
|
||||
SOURCES += asApp/src/configMenuSub.c
|
||||
SOURCES += asApp/src/verify.c
|
||||
SOURCES += asApp/src/asVerify.c
|
||||
SOURCES += asApp/src/os/Linux/osdNfs.c
|
||||
|
||||
HEADERS += asApp/src/os/Linux/osdNfs.h
|
||||
|
||||
DBDS += asApp/src/asSupport.dbd
|
17
MakeScaler.RHEL7
Normal file
17
MakeScaler.RHEL7
Normal file
@ -0,0 +1,17 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=scaler
|
||||
|
||||
BUILDCLASSES=Linux
|
||||
|
||||
USR_DEPENDENCIES = asyn,427.0.1
|
||||
|
||||
SOURCES += stdApp/src/scalerRecord.c
|
||||
SOURCES += stdApp/src/devScalerAsyn.c
|
||||
SOURCES += stdApp/src/drvScalerSoft.c
|
||||
|
||||
HEADERS += devScaler.h
|
||||
|
||||
|
||||
DBDS += stdApp/src/scalerRecord.dbd
|
||||
DBDS += stdApp/src/scalerSupport.dbd
|
@ -2,12 +2,13 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=sinq
|
||||
|
||||
BUILDCLASSES=Linux
|
||||
EPICS_VERSIONS=3.14.12 7.0.4.1
|
||||
|
||||
# additional module dependencies
|
||||
REQUIRED+=SynApps
|
||||
REQUIRED+=stream
|
||||
REQUIRED+=scaler
|
||||
|
||||
# using a test version
|
||||
scaler_VERSION=koennecke
|
||||
@ -26,8 +27,10 @@ SOURCES += sinqEPICSApp/src/EL734Driver.cpp
|
||||
SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/stptok.cpp
|
||||
SOURCES += sinqEPICSApp/src/PhytronDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c
|
||||
SOURCES += sinqEPICSApp/src/pmacAxis.cpp
|
||||
SOURCES += sinqEPICSApp/src/pmacController.cpp
|
||||
|
||||
SOURCES += sinqEPICSApp/src/drvAsynMasterMACSPort.c
|
||||
SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp
|
||||
# MISCS would be the place to keep the stream device template files
|
||||
|
37
Makefile.RHEL7.michele
Normal file
37
Makefile.RHEL7.michele
Normal file
@ -0,0 +1,37 @@
|
||||
# This build the sinq extensions for the PSI EPICS setup
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=sinq
|
||||
LIBVERSION=brambilla_m_test
|
||||
BUILDCLASSES=Linux
|
||||
EPICS_VERSIONS=3.14.12 7.0.4.1
|
||||
ARCH_FILTER=RHEL7-x86_64
|
||||
|
||||
# additional module dependencies
|
||||
REQUIRED+=SynApps
|
||||
REQUIRED+=stream
|
||||
REQUIRED+=scaler
|
||||
|
||||
# using a test version
|
||||
scaler_VERSION=koennecke
|
||||
|
||||
TEMPLATES += sinqEPICSApp/Db/dimetix.db
|
||||
TEMPLATES += sinqEPICSApp/Db/slsvme.db
|
||||
TEMPLATES += sinqEPICSApp/Db/spsamor.db
|
||||
|
||||
DBDS += sinqEPICSApp/src/sinq.dbd
|
||||
|
||||
# What we need at SINQ
|
||||
SOURCES += sinqEPICSApp/src/devScalerEL737.c
|
||||
SOURCES += sinqEPICSApp/src/SINQController.cpp
|
||||
SOURCES += sinqEPICSApp/src/SINQAxis.cpp
|
||||
SOURCES += sinqEPICSApp/src/EL734Driver.cpp
|
||||
SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/stptok.cpp
|
||||
SOURCES += sinqEPICSApp/src/PhytronDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c
|
||||
SOURCES += sinqEPICSApp/src/pmacAxis.cpp
|
||||
SOURCES += sinqEPICSApp/src/pmacController.cpp
|
||||
|
||||
# MISCS would be the place to keep the stream device template files
|
51
Makefile.RHEL8
Normal file
51
Makefile.RHEL8
Normal file
@ -0,0 +1,51 @@
|
||||
# This build the sinq extensions for the PSI EPICS setup
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=sinq
|
||||
BUILDCLASSES=Linux
|
||||
EPICS_VERSIONS=7.0.7
|
||||
ARCH_FILTER=RHEL%
|
||||
|
||||
# additional module dependencies
|
||||
REQUIRED+=SynApps
|
||||
REQUIRED+=stream
|
||||
REQUIRED+=scaler
|
||||
REQUIRED+=asynMotor
|
||||
REQUIRED+=calc
|
||||
|
||||
# Release version
|
||||
LIBVERSION=2024-ed-dev
|
||||
|
||||
# DB files to include in the release
|
||||
TEMPLATES += sinqEPICSApp/Db/counterbox_common.db
|
||||
TEMPLATES += sinqEPICSApp/Db/counterbox.db
|
||||
TEMPLATES += sinqEPICSApp/Db/counterbox_v2.db
|
||||
TEMPLATES += sinqEPICSApp/Db/counterbox.proto
|
||||
TEMPLATES += sinqEPICSApp/Db/dimetix.db
|
||||
TEMPLATES += sinqEPICSApp/Db/slsvme.db
|
||||
TEMPLATES += sinqEPICSApp/Db/spsamor.db
|
||||
|
||||
# DBD files to include in the release
|
||||
DBDS += sinqEPICSApp/src/sinq.dbd
|
||||
|
||||
# Source files to build
|
||||
SOURCES += sinqEPICSApp/src/EL734Driver.cpp
|
||||
SOURCES += sinqEPICSApp/src/EuroMoveDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/NanotecDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/PhytronDriver.cpp
|
||||
SOURCES += sinqEPICSApp/src/SINQAxis.cpp
|
||||
SOURCES += sinqEPICSApp/src/SINQController.cpp
|
||||
SOURCES += sinqEPICSApp/src/devScalerEL737.c
|
||||
SOURCES += sinqEPICSApp/src/pmacAsynIPPort.c
|
||||
SOURCES += sinqEPICSApp/src/pmacAxis.cpp
|
||||
SOURCES += sinqEPICSApp/src/pmacController.cpp
|
||||
SOURCES += sinqEPICSApp/src/stptok.cpp
|
||||
SOURCES += sinqEPICSApp/src/CounterBox.cpp
|
||||
|
||||
SCRIPTS += sinqEPICSApp/scripts/counterbox.cmd
|
||||
SCRIPTS += sinqEPICSApp/scripts/counterbox_v2.cmd
|
||||
|
||||
USR_CFLAGS += -Wall -Wextra # -Werror
|
||||
|
||||
# MISCS would be the place to keep the stream device template files
|
49
README.md
Normal file
49
README.md
Normal file
@ -0,0 +1,49 @@
|
||||
# SinqEPICSApp
|
||||
|
||||
This is an IOC application which contains the drivers for SINQ hardware.
|
||||
EPICS purists may find it messy because it contains the drivers for all
|
||||
SINQ stuff in one package rather then in separate modules. This is just
|
||||
for ease of distribution. The idea being that all of SINQ shares one
|
||||
module/IOC application and configures in its startup file what it needs
|
||||
of it.
|
||||
|
||||
Drivers included:
|
||||
|
||||
- EL734 motor controller
|
||||
- SINQ Delta Tau PMAC motor controller. This is a modified version of the Diamond
|
||||
PMAC code. The modification is necessary because PSI runs a different program in
|
||||
the MCU at SINQ then Diamond
|
||||
- Nanotec bus motor driver
|
||||
- Phytron MCU200 motor driver
|
||||
- EL737 counter box driver through the scaler record
|
||||
|
||||
There are some streamdevice protocol files provided:
|
||||
- spss5: for the Siemens SPS5 with the PSI custom RS232 module
|
||||
- slsvme: for the SLS magnet controllers managed by the SINQ special vmemaggi program
|
||||
- el755: the old SINQ magnet controller
|
||||
- dimetix: for the deimetix distance measurement device at AMOR
|
||||
|
||||
There are two main branches here:
|
||||
|
||||
- Of course master from which you can build the PSI RHEL7 module and the EEE module
|
||||
- The amorsim branch in which you can build the IOC application for ESS against the ICS
|
||||
setup. This is needed for the sinq-amorsim simulation. This branch exists because of the
|
||||
political problem to get a module into the ICS EEE setup.
|
||||
|
||||
Those political problems require a special development model:
|
||||
|
||||
1. If you do something, do it in a separate branch
|
||||
2. When reasonably sure that what you did works, merge it both into master and amorsim
|
||||
|
||||
Take care of the sinqEPICsApp/src/sinq.dbd file. This is the one which differs mostly between
|
||||
amorsim and master branches.
|
||||
|
||||
# Formatting
|
||||
|
||||
Formatting is done via the [`.clang-format`](./.clang-format) file checked into
|
||||
the repository. One option to apply the formatting to a given file is via the
|
||||
command below.
|
||||
|
||||
```
|
||||
clang-format -i -style=file <file>
|
||||
```
|
@ -25,17 +25,20 @@ TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top
|
||||
#SNCSEQ=$(EPICS_BASE)/../modules/soft/seq
|
||||
|
||||
# EPICS_BASE usually appears last so other apps can override stuff:
|
||||
EPICS_BASE=/opt/epics/bases/base-3.14.12.5
|
||||
|
||||
#EPICS_BASE=
|
||||
#MODULES=/home/epics/modules
|
||||
|
||||
# Set RULES here if you want to take build rules from somewhere
|
||||
# other than EPICS_BASE:
|
||||
#RULES=/path/to/epics/support/module/rules/x-y
|
||||
MOTOR=/opt/epics/modules/motor/6.10.0/3.14.12.5
|
||||
ASYN=/opt/epics/modules/asyn/4.27.0/3.14.12.5
|
||||
SYNAPPSSTD=/opt/epics/modules/synAppsStd/3.4.1/3.14.12.5/
|
||||
#ANC=/usr/local/epics/anc350v17
|
||||
STREAMS=/opt/epics/modules/streamdevice/2.6.0/3.14.12.5
|
||||
#LAKESHORE336=/usr/local/epics/support/lakeshore336
|
||||
BUSY=/opt/epics/modules/busy/1.6.0/3.14.12.5
|
||||
#OXINSTCRYOJET=/usr/local/epics/support/OxInstCryojet-2-18-3
|
||||
PCRE=/opt/epics/modules/pcre/8.36.0/3.14.12.5
|
||||
##RULES=/path/to/epics/support/module/rules/x-y
|
||||
#MOTOR=/opt/epics/modules/motor/6.10.0/3.14.12.5
|
||||
#ASYN=/opt/epics/modules/asyn/4.27.0/3.14.12.5
|
||||
#SYNAPPSSTD=/opt/epics/modules/synAppsStd/3.4.1/3.14.12.5/
|
||||
##ANC=/usr/local/epics/anc350v17
|
||||
#STREAMS=/opt/epics/modules/streamdevice/2.6.0/3.14.12.5
|
||||
##LAKESHORE336=/usr/local/epics/support/lakeshore336
|
||||
#BUSY=/opt/epics/modules/busy/1.6.0/3.14.12.5
|
||||
##OXINSTCRYOJET=/usr/local/epics/support/OxInstCryojet-2-18-3
|
||||
#PCRE=/opt/epics/modules/pcre/8.36.0/3.14.12.5
|
||||
|
||||
|
23
iocBoot/iocsinqEPICS/euromovetest.cmd
Executable file
23
iocBoot/iocsinqEPICS/euromovetest.cmd
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/local/bin/iocsh
|
||||
|
||||
require sinq,koennecke
|
||||
|
||||
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp/iocBoot/iocsinqEPICS")
|
||||
epicsEnvSet("BASE","/afs/psi.ch/project/sinqdev/sinqepicsapp")
|
||||
|
||||
cd ${TOP}
|
||||
|
||||
|
||||
|
||||
drvAsynIPPortConfigure("serial1", "129.129.195.83:1234",0,0,0)
|
||||
#drvAsynIPPortConfigure("serial1", "amor-ts:3002",0,0,0)
|
||||
#drvAsynIPPortConfigure("serial1", "localhost:9090",0,0,0)
|
||||
EuroMoveCreateController("llb","serial1", 10, 1);
|
||||
|
||||
### Motors
|
||||
|
||||
dbLoadRecords("$(BASE)/sinqEPICSApp/Db/asynRecord.db","P=KM36:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
|
||||
dbLoadTemplate "motor.substitutions.eurollb"
|
||||
|
||||
|
||||
iocInit
|
@ -1,4 +1,4 @@
|
||||
file "$(MOTOR)/db/basic_asyn_motor.db"
|
||||
qfile "$(MOTOR)/db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}
|
||||
|
13
iocBoot/iocsinqEPICS/motor.substitutions.eurollb
Normal file
13
iocBoot/iocsinqEPICS/motor.substitutions.eurollb
Normal file
@ -0,0 +1,13 @@
|
||||
file "$(BASE)/sinqEPICSApp/Db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, RDBD, DHLM, DLLM, INIT}
|
||||
{KM36:llb:, 1, "m$(N)", "asynMotor", llb, 1, "m1", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .01, 3, 0.2, 1000, 0, "1"}
|
||||
}
|
||||
|
||||
file "$(BASE)/sinqEPICSApp/Db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{KM36:llb:, 1, "m$(N)",llb}
|
||||
}
|
@ -1,7 +1,15 @@
|
||||
file "$(MOTOR)/db/basic_asyn_motor.db"
|
||||
file "$(BASE)/sinqEPICSApp/Db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}
|
||||
{KM36:phytron:, 1, "m$(N)", "asynMotor", phy, 1, "m1", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 100, -100, "1"}
|
||||
{KM36:phytron:, 2, "m$(N)", "asynMotor", phy, 2, "m2", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 100, -100, "10"}
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, RDBD, DHLM, DLLM, INIT}
|
||||
{KM36:phytron:, 1, "m$(N)", "asynMotor", phy, 1, "m1", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 0.2, 100, -100, "1"}
|
||||
{KM36:phytron:, 2, "m$(N)", "asynMotor", phy, 2, "m2", mm, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 0.2, 100, -100, "10"}
|
||||
}
|
||||
|
||||
file "$(BASE)/sinqEPICSApp/Db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{KM36:phytron:, 1, "m$(N)",phy}
|
||||
{KM36:phytron:, 2, "m$(N)",phy}
|
||||
}
|
25
iocBoot/iocsinqEPICS/phytest.cmd
Executable file
25
iocBoot/iocsinqEPICS/phytest.cmd
Executable file
@ -0,0 +1,25 @@
|
||||
#!/usr/local/bin/iocsh
|
||||
|
||||
require sinq,koennecke
|
||||
|
||||
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp/iocBoot/iocsinqEPICS")
|
||||
epicsEnvSet("BASE","/afs/psi.ch/project/sinqdev/sinqepicsapp")
|
||||
epicsEnvSet("dbPATH","${EPICS_BASE}/dbd:${ASYN}/dbd:${MOTOR}/dbd")
|
||||
|
||||
cd ${TOP}
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase "../../dbd/sinqEPICS.dbd"
|
||||
|
||||
|
||||
drvAsynIPPortConfigure("serial1", "129.129.195.58:22222",0,0,0)
|
||||
#drvAsynIPPortConfigure("serial1", "localhost:9090",0,0,0)
|
||||
PhytronCreateController("phy","serial1","0",1,0);
|
||||
|
||||
### Motors
|
||||
|
||||
dbLoadRecords("$(BASE)/sinqEPICSApp/Db/asynRecord.db","P=KM36:,R=serial1,PORT=serial1,ADDR=0,OMAX=80,IMAX=80")
|
||||
dbLoadTemplate "motor.substitutions.phytron"
|
||||
|
||||
|
||||
iocInit
|
@ -18,19 +18,16 @@ epicsEnvSet("dbPATH","${EPICS_BASE}/dbd::${ASYN}/dbd:${MOTOR}/dbd:${ANC}/dbd")
|
||||
cd ${TOP}
|
||||
|
||||
## Register all support components
|
||||
dbLoadDatabase "../../dbd/sinqEPICS.dbd"
|
||||
#dbLoadDatabase "dbd/sinq.dbd"
|
||||
dbLoadDatabase "../../sinqEPICSApp/src/sinq.dbd"
|
||||
sinqEPICS_registerRecordDeviceDriver pdbbase
|
||||
|
||||
## Load record instances
|
||||
#dbLoadRecords("db/xxx.db","user=koenneckeHost")
|
||||
|
||||
|
||||
#---------- load Nanotec motor controller
|
||||
#drvAsynIPPortConfigure("serial1", "narziss-ts:3002",0,0,0)
|
||||
#drvAsynIPPortConfigure("serial1", "localhost:5050",0,0,0)
|
||||
drvAsynIPPortConfigure("serial1", "localhost:8080",0,0,0)
|
||||
PhytronCreateController("phy","serial1",1,1);
|
||||
PhytronCreateController("phy","serial1","0",1,1);
|
||||
|
||||
### Motors
|
||||
|
||||
|
94
iocBoot/iocsinqEPICS/slsvme.db
Normal file
94
iocBoot/iocsinqEPICS/slsvme.db
Normal file
@ -0,0 +1,94 @@
|
||||
# Database definition for the SLS VME magnets as installed at AMOR
|
||||
|
||||
##
|
||||
## Read the High Limit
|
||||
##
|
||||
record(ai, "$(PREFIX)HighLim") {
|
||||
field(DTYP, "stream")
|
||||
field(DESC, "High Current Limit")
|
||||
field(INP, "@slsvme.proto read($(NO),hl) slsvme 0")
|
||||
field(SCAN, "1 second")
|
||||
field(PREC, "3")
|
||||
field(EGU, "A")
|
||||
}
|
||||
|
||||
##
|
||||
## Read the High Limit
|
||||
##
|
||||
record(ai, "$(PREFIX)LowLim") {
|
||||
field(DTYP, "stream")
|
||||
field(DESC, "Low Current Limit")
|
||||
field(INP, "@slsvme.proto read($(NO),ll) slsvme 0")
|
||||
field(SCAN, "1 second")
|
||||
field(PREC, "3")
|
||||
field(EGU, "A")
|
||||
}
|
||||
|
||||
##
|
||||
## Read the Error code
|
||||
##
|
||||
record(ai, "$(PREFIX)ErrCode") {
|
||||
field(DTYP, "stream")
|
||||
field(DESC, "Error Code")
|
||||
field(INP, "@slsvme.proto read($(NO),err) slsvme 0")
|
||||
field(SCAN, "1 second")
|
||||
field(PREC, "3")
|
||||
field(EGU, "A")
|
||||
}
|
||||
|
||||
##
|
||||
## Read the textual representation of the error.
|
||||
##
|
||||
record(stringin, "$(PREFIX)ErrText") {
|
||||
field(DTYP, "stream")
|
||||
field(INP, "@slsvme.proto readErrTxt($(NO)) slsvme 0")
|
||||
field(SCAN, "1 second")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
## Read the Current
|
||||
##
|
||||
record(ai, "$(PREFIX)CurRBV") {
|
||||
field(DTYP, "stream")
|
||||
field(DESC, "Low Current Limit")
|
||||
field(INP, "@slsvme.proto read($(NO),cur) slsvme 0")
|
||||
field(SCAN, "1 second")
|
||||
field(PREC, "3")
|
||||
field(EGU, "A")
|
||||
}
|
||||
|
||||
##
|
||||
## Set the current
|
||||
##
|
||||
record(ao, "$(PREFIX)CurSet") {
|
||||
field(DTYP, "stream")
|
||||
field(DESC, "Setpoint current")
|
||||
field(OUT, "@slsvme.proto write($(NO),cur) slsvme 0")
|
||||
field(PREC, "3")
|
||||
field(EGU, "A")
|
||||
}
|
||||
|
||||
##
|
||||
## Read power status of the magnet
|
||||
##
|
||||
record(bi, "$(PREFIX)PowerStatusRBV") {
|
||||
field(DESC, "Readback of the power status")
|
||||
field(DTYP, "stream")
|
||||
field(INP, "@slsvme.proto readonoff($(NO)) slsvme 0")
|
||||
field(SCAN, "1 second")
|
||||
field(ZNAM, "off")
|
||||
field(ONAM, "on")
|
||||
}
|
||||
|
||||
##
|
||||
## Set the power status
|
||||
##
|
||||
record(bo, "$(PREFIX)PowerStatus") {
|
||||
field(DESC, "Set the power status")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@slsvme.proto setpower($(NO)) slsvme 0")
|
||||
field(ZNAM, "on")
|
||||
field(ONAM, "off")
|
||||
}
|
@ -4,4 +4,4 @@ set dir [pwd]
|
||||
|
||||
cd /usr/local/ioc
|
||||
|
||||
exec /usr/bin/tar czvf $dir/sinqepics.tgz modules/scaler modules/sinq modules/anc350
|
||||
exec /usr/bin/tar czvf $dir/sinqepics.tgz modules/scaler modules/sinq modules/anc350 modules/autosave
|
||||
|
9
sinqEPICSApp/Db/asynRecord.db
Normal file
9
sinqEPICSApp/Db/asynRecord.db
Normal file
@ -0,0 +1,9 @@
|
||||
record(asyn,"$(P)$(R)")
|
||||
{
|
||||
field(DTYP,"asynRecordDevice")
|
||||
field(PORT,"$(PORT)")
|
||||
field(ADDR,"$(ADDR)")
|
||||
field(OMAX,"$(OMAX)")
|
||||
field(IMAX,"$(IMAX)")
|
||||
}
|
||||
|
24
sinqEPICSApp/Db/basic_asyn_motor.db
Normal file
24
sinqEPICSApp/Db/basic_asyn_motor.db
Normal file
@ -0,0 +1,24 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VMAX,"$(VMAX=0)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
field(RTRY,"0")
|
||||
}
|
||||
|
21
sinqEPICSApp/Db/basic_motor.db
Normal file
21
sinqEPICSApp/Db/basic_motor.db
Normal file
@ -0,0 +1,21 @@
|
||||
grecord(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"#C$(C) S$(S) @")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
}
|
||||
|
46
sinqEPICSApp/Db/counterbox.db
Normal file
46
sinqEPICSApp/Db/counterbox.db
Normal file
@ -0,0 +1,46 @@
|
||||
# EL737 EPICS Database for streamdevice support
|
||||
# Macros
|
||||
# P - Prefix
|
||||
# NAME - just a name, e.g. EL737
|
||||
# PROTO - Stream device protocol file
|
||||
# ASYN_PORT - Low level Asyn IP Port to EL737
|
||||
|
||||
################################################################################
|
||||
# Status Variables
|
||||
|
||||
record(longin, "$(P):$(NAME):MONITOR-CHANNEL")
|
||||
{
|
||||
field(DESC, "PRESET-COUNT Monitors this channel")
|
||||
field(VAL, 1)
|
||||
field(DISP, 1)
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):MONITOR-CHANNEL_RBV")
|
||||
{
|
||||
field(DESC, "PRESET-COUNT Monitors this channel")
|
||||
field(VAL, 1)
|
||||
field(DISP, 1)
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Count Commands
|
||||
|
||||
record(longout,"$(P):$(NAME):THRESHOLD-MONITOR")
|
||||
{
|
||||
field(DESC, "Channel monitored for minimum rate")
|
||||
field(VAL, "1") # Monitor
|
||||
field(DRVL, "1") # Smallest Threshold Channel
|
||||
field(DRVL, "8") # Largest Threshold Channel
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Read all monitors values
|
||||
|
||||
record(ai, "$(P):$(NAME):READALL")
|
||||
{
|
||||
field(DESC, "Reads monitors and elapsed time")
|
||||
field(INP, "@$(PROTO) readAll8($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(SCAN, ".2 second")
|
||||
field(DTYP, "stream")
|
||||
field(FLNK, "$(P):$(NAME):UNSET-COUNTING")
|
||||
}
|
128
sinqEPICSApp/Db/counterbox.proto
Normal file
128
sinqEPICSApp/Db/counterbox.proto
Normal file
@ -0,0 +1,128 @@
|
||||
#
|
||||
# Counterbox Protocol File
|
||||
#
|
||||
OutTerminator = CR;
|
||||
InTerminator = CR;
|
||||
ReadTimeout = 100;
|
||||
WriteTimeout = 100;
|
||||
ReplyTimeout = 200;
|
||||
LockTimeout = 450;
|
||||
|
||||
initialise {
|
||||
out "RMT 1";
|
||||
in;
|
||||
out "ECHO 2";
|
||||
in "%(\$1MsgTxt)s"; # Clear MsgTxt on Init
|
||||
@mismatch{
|
||||
exec 'echo "Failed to configure counterbox" && exit(1)';
|
||||
}
|
||||
}
|
||||
|
||||
fullReset {
|
||||
out "\%";
|
||||
wait 5000;
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Status Variables
|
||||
|
||||
readStatus {
|
||||
out "RS";
|
||||
in "%d";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
readPresetMonitor {
|
||||
out "PC";
|
||||
in "%d";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
writePresetMonitor {
|
||||
out "PC %d";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Count Commands
|
||||
|
||||
startWithCountPreset {
|
||||
out "MP %d";
|
||||
in;
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
startWithTimePreset {
|
||||
out "TP %#.2f";
|
||||
in;
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
pauseCount {
|
||||
out "PS";
|
||||
in;
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
continueCount {
|
||||
out "CO";
|
||||
in;
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
stopCount {
|
||||
out "S";
|
||||
in;
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
setMinRate{
|
||||
out "DL %(\$1THRESHOLD-MONITOR)d %(\$1THRESHOLD)d";
|
||||
in;
|
||||
out "DR %(\$1THRESHOLD-MONITOR)d";
|
||||
in;
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
readMinRate{
|
||||
out "DR";
|
||||
in "%(\$1THRESHOLD-MONITOR_RBV)d";
|
||||
out "DL %(\$1THRESHOLD-MONITOR_RBV)d";
|
||||
in "%(\$1THRESHOLD_RBV)d";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Read Values From Monitors
|
||||
|
||||
readAll8 {
|
||||
out "RA";
|
||||
in "%(\$1ELAPSED-TIME)f %(\$1M1)d %(\$1M2)d %(\$1M3)d %(\$1M4)d %(\$1M5)d %(\$1M6)d %(\$1M7)d %(\$1M8)d";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
readAll10 {
|
||||
out "RA";
|
||||
in "%(\$1ELAPSED-TIME)f %(\$1M1)d %(\$1M2)d %(\$1M3)d %(\$1M4)d %(\$1M5)d %(\$1M6)d %(\$1M7)d %(\$1M8)d %(\$1M9)d %(\$1M10)d";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
readRate {
|
||||
out "RR \$2";
|
||||
in "%(\$1R\$2)f";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Testing Commands
|
||||
|
||||
switchTestgenOnOff {
|
||||
out "TG %{off|on}";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
||||
|
||||
# Only suppporting test channel 1 at the moment. (The first argument to TG)
|
||||
setTestSignal {
|
||||
out "TG 1 %(\$1TESTGEN-HIGHRATE)d %(\$1TESTGEN-LOWRATE)d";
|
||||
@mismatch{in "%(\$1MsgTxt)s";}
|
||||
}
|
259
sinqEPICSApp/Db/counterbox_common.db
Normal file
259
sinqEPICSApp/Db/counterbox_common.db
Normal file
@ -0,0 +1,259 @@
|
||||
# EL737 EPICS Database for streamdevice support
|
||||
# Macros
|
||||
# P - Prefix
|
||||
# NAME - just a name, e.g. EL737
|
||||
# PROTO - Stream device protocol file
|
||||
# ASYN_PORT - Low level Asyn IP Port to EL737
|
||||
|
||||
# Send initial initialisation commands
|
||||
record(bo, "$(P):$(NAME):INIT-CONF")
|
||||
{
|
||||
field(DESC, "Initialises the Counterbox")
|
||||
field(OUT, "@$(PROTO) initialise($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(PINI, "YES") # Run at init
|
||||
field(DTYP, "stream")
|
||||
field(FLNK, "$(P):$(NAME):INIT-BOX")
|
||||
}
|
||||
|
||||
# As we aren't certain of the order that PINI exectutes PVs, we only set it to
|
||||
# true on INIT-CONF to make sure the box is ready to receive commands, and then
|
||||
# let INIT-CONF trigger the initialisation of other necessary records
|
||||
record(fanout, "$(P):$(NAME):INIT-BOX")
|
||||
{
|
||||
field(DESC, "Rewrite PVs to Box")
|
||||
field(SELM, "All")
|
||||
field(LNK0, "$(P):$(NAME):MONITOR-CHANNEL_RBV PP")
|
||||
field(LNK1, "$(P):$(NAME):READALL PP")
|
||||
field(LNK2, "$(P):$(NAME):THRESHOLD_RBV PP")
|
||||
}
|
||||
|
||||
record(longout, "$(P):$(NAME):FULL-RESET")
|
||||
{
|
||||
field(DESC, "Reset the Counterbox")
|
||||
field(OUT, "@$(PROTO) fullReset($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(DTYP, "stream")
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Status Variables
|
||||
|
||||
record(stringin, "$(P):$(NAME):MsgTxt")
|
||||
{
|
||||
field(DESC, "Unexpected received response")
|
||||
field(DTYP, "devCounterBoxStringError")
|
||||
field(FLNK, "$(P):$(NAME):INVALID-CONFIG")
|
||||
}
|
||||
|
||||
# We want to recognise the invalid config error message, so that we can rerun
|
||||
# the init if it occurs. This should only happen after turning the box off and
|
||||
# on again or running a full reset
|
||||
record(scalcout, "$(P):$(NAME):INVALID-CONFIG")
|
||||
{
|
||||
field(DESC, "Has the counterbox been configured?")
|
||||
field(CALC, "AA[0,2] == '?OF'")
|
||||
field(INAA, "$(P):$(NAME):MsgTxt")
|
||||
field(FLNK, "$(P):$(NAME):REINIT-CONF")
|
||||
}
|
||||
|
||||
record(seq, "$(P):$(NAME):REINIT-CONF")
|
||||
{
|
||||
field(LNK1, "$(P):$(NAME):INIT-CONF PP")
|
||||
field(DO1, 1)
|
||||
field(SELM, "Specified")
|
||||
field(SELL, "$(P):$(NAME):INVALID-CONFIG.VAL")
|
||||
}
|
||||
|
||||
# The COUNTING PV stays True until Counterbox has switched back to idle mode
|
||||
# and the monitor counts have been read. Therefore, we know that the monitor
|
||||
# values have been updated to represent their final values, when this switches
|
||||
# back to False.
|
||||
#
|
||||
# This is accomplished via the explicit SET-COUNTING and UNSET-COUNTING seq
|
||||
# records, that are triggered by a switch to the counting status
|
||||
# (RAW-STATUS == 1 || 2) and a read of the monitors respectively.
|
||||
record(bi, "$(P):$(NAME):COUNTING")
|
||||
{
|
||||
field(DESC, "Counterbox is Counting")
|
||||
field(VAL, 0)
|
||||
}
|
||||
|
||||
record(seq, "$(P):$(NAME):SET-COUNTING")
|
||||
{
|
||||
field(LNK1, "$(P):$(NAME):COUNTING PP")
|
||||
field(DO1, 1)
|
||||
field(SELM, "Specified")
|
||||
field(SELL, "$(P):$(NAME):MAP-STATUS.VAL")
|
||||
field(FLNK, "$(P):$(NAME):STATUS")
|
||||
}
|
||||
|
||||
record(seq, "$(P):$(NAME):UNSET-COUNTING")
|
||||
{
|
||||
field(LNK0, "$(P):$(NAME):COUNTING PP")
|
||||
field(DO0, 0)
|
||||
field(SELM, "Specified")
|
||||
field(SELL, "$(P):$(NAME):RAW-STATUS.VAL")
|
||||
field(FLNK, "$(P):$(NAME):MAP-STATUS")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):RAW-STATUS")
|
||||
{
|
||||
field(DESC, "Raw returned status value")
|
||||
field(DTYP, "stream")
|
||||
field(SCAN, ".2 second")
|
||||
field(INP, "@$(PROTO) readStatus($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(FLNK, "$(P):$(NAME):MAP-STATUS")
|
||||
}
|
||||
|
||||
record(calc, "$(P):$(NAME):MAP-STATUS")
|
||||
{
|
||||
field(DESC, "Maps Raw Status to State")
|
||||
field(INPA, "$(P):$(NAME):RAW-STATUS NPP")
|
||||
field(INPB, "$(P):$(NAME):INVALID-CONFIG NPP")
|
||||
field(INPC, "$(P):$(NAME):COUNTING NPP")
|
||||
field(CALC, "B=1?4:(C=1&&A=0)||A=1||A=2?1:A=0?0:A=5||A=6?2:A=9||A=13||A=10||A=14?3:4")
|
||||
field(FLNK, "$(P):$(NAME):SET-COUNTING")
|
||||
}
|
||||
|
||||
record(mbbi, "$(P):$(NAME):STATUS")
|
||||
{
|
||||
field(DESC, "Counterbox Status")
|
||||
field(INP, "$(P):$(NAME):MAP-STATUS NPP")
|
||||
field(ZRVL, "0")
|
||||
field(ZRST, "Idle")
|
||||
field(ONVL, "1")
|
||||
field(ONST, "Counting")
|
||||
field(TWVL, "2")
|
||||
field(TWST, "Low rate")
|
||||
field(THVL, "3")
|
||||
field(THST, "Paused")
|
||||
# 4 should never happen, if it does it means the counter box reports undocumented statusbits
|
||||
field(FRVL, "4")
|
||||
field(FRST, "INVALID")
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Count Commands
|
||||
|
||||
record(ao,"$(P):$(NAME):PRESET-COUNT")
|
||||
{
|
||||
field(DESC, "Count until preset reached")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) startWithCountPreset($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(VAL, 0)
|
||||
field(PREC, 2)
|
||||
}
|
||||
|
||||
record(ao,"$(P):$(NAME):PRESET-TIME")
|
||||
{
|
||||
field(DESC, "Count for specified time")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) startWithTimePreset($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(VAL, 0)
|
||||
field(PREC, 2)
|
||||
field(EGU, "seconds")
|
||||
}
|
||||
|
||||
record(bo,"$(P):$(NAME):PAUSE")
|
||||
{
|
||||
field(DESC, "Pause the current count")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) pauseCount($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(VAL, "0")
|
||||
}
|
||||
|
||||
record(bo,"$(P):$(NAME):CONTINUE")
|
||||
{
|
||||
field(DESC, "Continue with a count that was paused")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) continueCount($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(VAL, "0")
|
||||
}
|
||||
|
||||
record(bo, "$(P):$(NAME):STOP")
|
||||
{
|
||||
field(DESC, "Stop the current counting operation")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) stopCount($(P):$(NAME):) $(ASYN_PORT)")
|
||||
}
|
||||
|
||||
# TODO should changing the monitor also set things?
|
||||
# or only when actually setting a threshold?
|
||||
record(longout,"$(P):$(NAME):THRESHOLD")
|
||||
{
|
||||
field(DESC, "Minimum rate for counting to proceed")
|
||||
field(VAL, "0") # Rate
|
||||
field(DRVL, "0") # Minimum Rate
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) setMinRate($(P):$(NAME):) $(ASYN_PORT)")
|
||||
}
|
||||
|
||||
record(longin,"$(P):$(NAME):THRESHOLD_RBV")
|
||||
{
|
||||
field(DESC, "Minimum rate for counting to proceed")
|
||||
field(DTYP, "stream")
|
||||
field(INP, "@$(PROTO) readMinRate($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(SCAN, "2 second")
|
||||
}
|
||||
|
||||
record(longin,"$(P):$(NAME):THRESHOLD-MONITOR_RBV")
|
||||
{
|
||||
field(DESC, "Channel monitored for minimum rate")
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Read all monitors values
|
||||
|
||||
record(ai,"$(P):$(NAME):ELAPSED-TIME")
|
||||
{
|
||||
field(DESC, "Counterbox Measured Time")
|
||||
field(EGU, "seconds")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M1")
|
||||
{
|
||||
field(DESC, "Counterbox CH1")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M2")
|
||||
{
|
||||
field(DESC, "Counterbox CH2")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M3")
|
||||
{
|
||||
field(DESC, "Counterbox CH3")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M4")
|
||||
{
|
||||
field(DESC, "Counterbox CH4")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M5")
|
||||
{
|
||||
field(DESC, "Counterbox CH5")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M6")
|
||||
{
|
||||
field(DESC, "Counterbox CH6")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M7")
|
||||
{
|
||||
field(DESC, "Counterbox CH7")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M8")
|
||||
{
|
||||
field(DESC, "Counterbox CH8")
|
||||
}
|
||||
|
||||
# Not yet sure whether we want to support this
|
||||
# record(longin, "$(P):$(NAME):R1")
|
||||
# {
|
||||
# field(DESC, "Counterbox Rate CH1")
|
||||
# field(INP, "@$(PROTO) readRate($(P):$(NAME):, 1) $(ASYN_PORT)")
|
||||
# field(SCAN, ".2 second")
|
||||
# field(DTYP, "stream")
|
||||
# }
|
88
sinqEPICSApp/Db/counterbox_v2.db
Normal file
88
sinqEPICSApp/Db/counterbox_v2.db
Normal file
@ -0,0 +1,88 @@
|
||||
# EL737 EPICS Database for streamdevice support
|
||||
# Macros
|
||||
# P - Prefix
|
||||
# NAME - just a name, e.g. DAQV2
|
||||
# PROTO - Stream device protocol file
|
||||
# ASYN_PORT - Low level Asyn IP Port to Counterbox
|
||||
|
||||
################################################################################
|
||||
# Status Variables
|
||||
|
||||
record(longout, "$(P):$(NAME):MONITOR-CHANNEL")
|
||||
{
|
||||
field(DESC, "PRESET-COUNT Monitors this channel")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) writePresetMonitor($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(FLNK, "$(P):$(NAME):MONITOR-CHANNEL_RBV")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):MONITOR-CHANNEL_RBV")
|
||||
{
|
||||
field(DESC, "PRESET-COUNT Monitors this channel")
|
||||
field(DTYP, "stream")
|
||||
field(INP, "@$(PROTO) readPresetMonitor($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(SCAN, "5 second")
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Count Commands
|
||||
|
||||
record(longout,"$(P):$(NAME):THRESHOLD-MONITOR")
|
||||
{
|
||||
field(DESC, "Channel monitored for minimum rate")
|
||||
field(VAL, "1") # Monitor
|
||||
field(DRVL, "1") # Smallest Threshold Channel
|
||||
field(DRVL, "10") # Largest Threshold Channel
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Read all monitors values
|
||||
|
||||
record(ai, "$(P):$(NAME):READALL")
|
||||
{
|
||||
field(DESC, "Reads monitors and elapsed time")
|
||||
field(INP, "@$(PROTO) readAll10($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(SCAN, ".2 second")
|
||||
field(DTYP, "stream")
|
||||
field(FLNK, "$(P):$(NAME):UNSET-COUNTING")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M9")
|
||||
{
|
||||
field(DESC, "Counterbox CH9")
|
||||
}
|
||||
|
||||
record(longin, "$(P):$(NAME):M10")
|
||||
{
|
||||
field(DESC, "Counterbox CH10")
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Testing Commands
|
||||
|
||||
# These won't match the values on the machine after a full restart But I chose
|
||||
# not to force their intialisation as they are only important for testing
|
||||
|
||||
record(bo, "$(P):$(NAME):TESTGEN")
|
||||
{
|
||||
field(DESC, "Turn on/off Testgen Signal")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) switchTestgenOnOff($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(VAL, 0)
|
||||
}
|
||||
|
||||
record(longout, "$(P):$(NAME):TESTGEN-LOWRATE")
|
||||
{
|
||||
field(DESC, "Set Minimum Testgen Rate")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) setTestSignal($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(VAL, 1000)
|
||||
}
|
||||
|
||||
record(longout, "$(P):$(NAME):TESTGEN-HIGHRATE")
|
||||
{
|
||||
field(DESC, "Set Maximum Testgen Rate")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@$(PROTO) setTestSignal($(P):$(NAME):) $(ASYN_PORT)")
|
||||
field(VAL, 1000)
|
||||
}
|
@ -6,4 +6,13 @@ record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
}
|
||||
|
||||
record(ao,"$(P)m$(N)-Resolution"){
|
||||
field(DESC,"m$(N) Resolution")
|
||||
field(DOL,"$(P)m$(N).MRES CP MS")
|
||||
field(OMSL,"closed_loop")
|
||||
field(DTYP,"asynFloat64")
|
||||
field(OUT,"@asyn($(PORT),$(N))MOTOR_RESOLUTION")
|
||||
field(PREC,"3")
|
||||
}
|
||||
|
13
sinqEPICSApp/Db/motorSet.db
Normal file
13
sinqEPICSApp/Db/motorSet.db
Normal file
@ -0,0 +1,13 @@
|
||||
# workaround over set position
|
||||
record(ao, "$(P)$(M)-SetPosition") {
|
||||
field(DTYP, "asynFloat64")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) SET_MOTOR_POSITION")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "YES")
|
||||
}
|
54
sinqEPICSApp/Db/sinq_asyn_motor.db
Normal file
54
sinqEPICSApp/Db/sinq_asyn_motor.db
Normal file
@ -0,0 +1,54 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VMAX,"$(VMAX=$(VELO))")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
}
|
||||
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longin, "$(P)$(M):Enable_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) AXIS_ENABLED")
|
||||
field(PINI, "YES")
|
||||
field(SCAN, "1 second")
|
||||
}
|
||||
|
||||
# reread encoder
|
||||
record(longout, "$(P)$(M):Reread_Encoder") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) REREAD_ENCODER_POSITION")
|
||||
field(PINI, "NO")
|
||||
}
|
13
sinqEPICSApp/scripts/counterbox.cmd
Normal file
13
sinqEPICSApp/scripts/counterbox.cmd
Normal file
@ -0,0 +1,13 @@
|
||||
require asyn
|
||||
|
||||
# Need to be set by user
|
||||
# epicsEnvSet("CNTBOX_HOST", "testinst-daq1:2000")
|
||||
# epicsEnvSet("ASYN_PORT", "el737")
|
||||
# epicsEnvSet("PREFIX", "SQ:SINQTEST")
|
||||
# epicsEnvSet("NAME", "COUNTERBOX")
|
||||
|
||||
epicsEnvSet("PROTO", "$(sinq_DB)counterbox.proto")
|
||||
|
||||
drvAsynIPPortConfigure("$(ASYN_PORT)", "$(CNTBOX_HOST)", 0, 0, 0)
|
||||
dbLoadRecords("$(sinq_DB)counterbox_common.db", "P=$(PREFIX), NAME=$(NAME), PROTO=$(PROTO), ASYN_PORT=$(ASYN_PORT)")
|
||||
dbLoadRecords("$(sinq_DB)counterbox.db", "P=$(PREFIX), NAME=$(NAME), PROTO=$(PROTO), ASYN_PORT=$(ASYN_PORT)")
|
13
sinqEPICSApp/scripts/counterbox_v2.cmd
Normal file
13
sinqEPICSApp/scripts/counterbox_v2.cmd
Normal file
@ -0,0 +1,13 @@
|
||||
require asyn
|
||||
|
||||
# Need to be set by user
|
||||
# epicsEnvSet("CNTBOX_HOST", "testinst-daq1:2000")
|
||||
# epicsEnvSet("ASYN_PORT", "el737")
|
||||
# epicsEnvSet("PREFIX", "SQ:SINQTEST")
|
||||
# epicsEnvSet("NAME", "COUNTERBOX")
|
||||
|
||||
epicsEnvSet("PROTO", "$(sinq_DB)counterbox.proto")
|
||||
|
||||
drvAsynIPPortConfigure("$(ASYN_PORT)", "$(CNTBOX_HOST)", 0, 0, 0)
|
||||
dbLoadRecords("$(sinq_DB)counterbox_common.db", "P=$(PREFIX), NAME=$(NAME), PROTO=$(PROTO), ASYN_PORT=$(ASYN_PORT)")
|
||||
dbLoadRecords("$(sinq_DB)counterbox_v2.db", "P=$(PREFIX), NAME=$(NAME), PROTO=$(PROTO), ASYN_PORT=$(ASYN_PORT)")
|
60
sinqEPICSApp/src/CounterBox.cpp
Normal file
60
sinqEPICSApp/src/CounterBox.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include <epicsExport.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <stringinRecord.h>
|
||||
|
||||
#define update_val(prec, new_val) \
|
||||
{ \
|
||||
static_assert(std::char_traits<char>::length((new_val)) < MAX_SIZE, \
|
||||
"New Val Exceeds Max String Length"); \
|
||||
strncpy((prec)->val, (new_val), MAX_SIZE); \
|
||||
}
|
||||
|
||||
static long map_raw_failure_message(struct stringinRecord *prec) {
|
||||
const uint8_t MAX_SIZE = 40; // including null terminator
|
||||
|
||||
if (strcmp(prec->val, "?OF") == 0) {
|
||||
update_val(prec, "?OF: Configuration Error");
|
||||
} else if (strcmp(prec->val, "?OV") == 0) {
|
||||
update_val(prec, "?OV: Overflow");
|
||||
} else if (strcmp(prec->val, "?1") == 0) {
|
||||
update_val(prec, "?1: Parameter out of range");
|
||||
} else if (strcmp(prec->val, "?2") == 0) {
|
||||
update_val(prec, "?2: Bad command");
|
||||
} else if (strcmp(prec->val, "?3") == 0) {
|
||||
update_val(prec, "?3: Bad parameter");
|
||||
} else if (strcmp(prec->val, "?4") == 0) {
|
||||
update_val(prec, "?4: Bad counter");
|
||||
} else if (strcmp(prec->val, "?5") == 0) {
|
||||
update_val(prec, "?5: Parameter missing");
|
||||
} else if (strcmp(prec->val, "?6") == 0) {
|
||||
update_val(prec, "?6: Too many counts");
|
||||
} else if (strcmp(prec->val, "?91") == 0) {
|
||||
update_val(prec, "?91: Start Failure");
|
||||
} else if (strcmp(prec->val, "?92") == 0) {
|
||||
update_val(prec, "?92: Failure while counting");
|
||||
} else if (strcmp(prec->val, "?93") == 0) {
|
||||
update_val(prec, "?93: Read Failure");
|
||||
} else if (strstr(prec->val, "?") != NULL) {
|
||||
char val_copy[40] = {0};
|
||||
strncpy(val_copy, prec->val, MAX_SIZE);
|
||||
snprintf(prec->val, MAX_SIZE - 1, "%s: HW error", val_copy);
|
||||
} else {
|
||||
// Leave the message as is
|
||||
}
|
||||
|
||||
return 0; // returns: (-1,0)=>(failure,success)
|
||||
}
|
||||
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN read_ai;
|
||||
DEVSUPFUN special_linconv;
|
||||
} devCounterBoxStringError = {
|
||||
6, NULL, NULL, NULL, NULL, (DEVSUPFUN)map_raw_failure_message, NULL};
|
||||
|
||||
epicsExportAddress(dset, devCounterBoxStringError);
|
@ -11,7 +11,6 @@ Mark Koennecke, May, August 2017
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@ -30,115 +29,111 @@ Mark Koennecke, May, August 2017
|
||||
|
||||
#define IDLEPOLL 60
|
||||
|
||||
|
||||
/** Creates a new EL734Controller object.
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] EL734PortName The name of the drvAsynSerialPort that was created previously to connect to the EL734 controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] EL734PortName The name of the drvAsynSerialPort that was created previously to connect to the EL734 controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
EL734Controller::EL734Controller(const char *portName, const char *EL734PortName, int numAxes)
|
||||
: SINQController(portName, EL734PortName, numAxes)
|
||||
: SINQController(portName, EL734PortName, numAxes)
|
||||
{
|
||||
int axis;
|
||||
asynStatus status;
|
||||
EL734Axis *pAxis;
|
||||
static const char *functionName = "EL734Controller::EL734Controller";
|
||||
|
||||
|
||||
/* Connect to EL734 controller */
|
||||
status = pasynOctetSyncIO->connect(EL734PortName, 0, &pasynUserController_, NULL);
|
||||
if (status) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: cannot connect to EL734 controller\n",
|
||||
functionName);
|
||||
if (status)
|
||||
{
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: cannot connect to EL734 controller\n",
|
||||
functionName);
|
||||
}
|
||||
pasynOctetSyncIO->setOutputEos(pasynUserController_,"\r",strlen("\r"));
|
||||
pasynOctetSyncIO->setInputEos(pasynUserController_,"\r",strlen("\r"));
|
||||
pasynOctetSyncIO->setOutputEos(pasynUserController_, "\r", strlen("\r"));
|
||||
pasynOctetSyncIO->setInputEos(pasynUserController_, "\r", strlen("\r"));
|
||||
|
||||
switchRemote();
|
||||
|
||||
for (axis=0; axis<numAxes; axis++) {
|
||||
pAxis = new EL734Axis(this, axis+1);
|
||||
for (axis = 0; axis < numAxes; axis++)
|
||||
{
|
||||
new EL734Axis(this, axis + 1);
|
||||
}
|
||||
|
||||
startPoller(1000./1000., IDLEPOLL, 2);
|
||||
startPoller(1000. / 1000., IDLEPOLL, 2);
|
||||
}
|
||||
|
||||
|
||||
/** Creates a new EL734Controller object.
|
||||
* Configuration command, called directly or from iocsh
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] EL734PortName The name of the drvAsynIPPPort that was created previously to connect to the EL734 controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
* Configuration command, called directly or from iocsh
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] EL734PortName The name of the drvAsynIPPPort that was created previously to connect to the EL734 controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
extern "C" int EL734CreateController(const char *portName, const char *EL734PortName, int numAxes)
|
||||
{
|
||||
EL734Controller *pEL734Controller
|
||||
= new EL734Controller(portName, EL734PortName, numAxes);
|
||||
EL734Controller *pEL734Controller = new EL734Controller(portName, EL734PortName, numAxes);
|
||||
pEL734Controller = NULL;
|
||||
return(asynSuccess);
|
||||
return (asynSuccess);
|
||||
}
|
||||
|
||||
/** Reports on status of the driver
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* If details > 0 then information is printed about each axis.
|
||||
* After printing controller-specific information it calls asynMotorController::report()
|
||||
*/
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* If details > 0 then information is printed about each axis.
|
||||
* After printing controller-specific information it calls asynMotorController::report()
|
||||
*/
|
||||
void EL734Controller::report(FILE *fp, int level)
|
||||
{
|
||||
fprintf(fp, "EL734 motor driver %s, numAxes=%d\n",
|
||||
this->portName, numAxes_);
|
||||
fprintf(fp, "EL734 motor driver %s, numAxes=%d\n",
|
||||
this->portName, numAxes_);
|
||||
|
||||
// Call the base class method
|
||||
asynMotorController::report(fp, level);
|
||||
}
|
||||
|
||||
/** Returns a pointer to an EL734Axis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
|
||||
EL734Axis* EL734Controller::getAxis(asynUser *pasynUser)
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
|
||||
EL734Axis *EL734Controller::getAxis(asynUser *pasynUser)
|
||||
{
|
||||
return static_cast<EL734Axis*>(asynMotorController::getAxis(pasynUser));
|
||||
return static_cast<EL734Axis *>(asynMotorController::getAxis(pasynUser));
|
||||
}
|
||||
|
||||
/** Returns a pointer to an EL734Axis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] axisNo Axis index number. */
|
||||
EL734Axis* EL734Controller::getAxis(int axisNo)
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] axisNo Axis index number. */
|
||||
EL734Axis *EL734Controller::getAxis(int axisNo)
|
||||
{
|
||||
return static_cast<EL734Axis*>(asynMotorController::getAxis(axisNo));
|
||||
return static_cast<EL734Axis *>(asynMotorController::getAxis(axisNo));
|
||||
}
|
||||
|
||||
void EL734Controller::switchRemote()
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
int status;
|
||||
size_t in, out;
|
||||
int reason;
|
||||
|
||||
strcpy(command,"RMT 1");
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(command,"ECHO 0");
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(command,"RMT 1");
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(command,"ECHO 0");
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(command, "RMT 1");
|
||||
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply, COMLEN, 1., &out, &in, &reason);
|
||||
strcpy(command, "ECHO 0");
|
||||
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply, COMLEN, 1., &out, &in, &reason);
|
||||
strcpy(command, "RMT 1");
|
||||
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply, COMLEN, 1., &out, &in, &reason);
|
||||
strcpy(command, "ECHO 0");
|
||||
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply, COMLEN, 1., &out, &in, &reason);
|
||||
}
|
||||
/**
|
||||
* send a command to the EL734 and read the reply. Do some error and controller
|
||||
* send a command to the EL734 and read the reply. Do some error and controller
|
||||
* issue fixing on the way
|
||||
* \param[in] command The command to send
|
||||
* \param[out] reply The controllers reply
|
||||
*/
|
||||
|
||||
asynStatus EL734Controller::transactController(int axisNo,char command[COMLEN], char reply[COMLEN])
|
||||
asynStatus EL734Controller::transactController(int axisNo, char command[COMLEN], char reply[COMLEN])
|
||||
{
|
||||
asynStatus status;
|
||||
size_t in, out, i;
|
||||
@ -147,58 +142,71 @@ asynStatus EL734Controller::transactController(int axisNo,char command[COMLEN],
|
||||
SINQAxis *axis = getAxis(axisNo);
|
||||
|
||||
pasynOctetSyncIO->flush(pasynUserController_);
|
||||
|
||||
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
if(status != asynSuccess){
|
||||
if(axis!= NULL){
|
||||
reply, COMLEN, 2., &out, &in, &reason);
|
||||
if (status != asynSuccess)
|
||||
{
|
||||
if (axis != NULL)
|
||||
{
|
||||
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
check for the offline reply
|
||||
*/
|
||||
if(strstr(reply,"?LOC") != NULL){
|
||||
if (strstr(reply, "?LOC") != NULL)
|
||||
{
|
||||
switchRemote();
|
||||
return pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
reply, COMLEN, 1., &out, &in, &reason);
|
||||
}
|
||||
|
||||
/*
|
||||
check for echos. This means that the thing is offline.
|
||||
*/
|
||||
if(strstr(reply,"p ") != NULL || strstr(reply,"u ") != NULL || strstr(reply,"msr ") != NULL){
|
||||
if (strstr(reply, "p ") != NULL || strstr(reply, "u ") != NULL || strstr(reply, "msr ") != NULL)
|
||||
{
|
||||
switchRemote();
|
||||
return pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
reply, COMLEN, 1., &out, &in, &reason);
|
||||
}
|
||||
/*
|
||||
check for EL734 errors
|
||||
*/
|
||||
strcpy(myReply, reply);
|
||||
for(i = 0; i < strlen(reply); i++){
|
||||
for (i = 0; i < strlen(reply); i++)
|
||||
{
|
||||
myReply[i] = (char)tolower((int)reply[i]);
|
||||
}
|
||||
if(strstr(myReply,"?cmd") != NULL){
|
||||
snprintf(errTxt,sizeof(errTxt), "Bad command %s at axis %d", command, axisNo);
|
||||
errlogSevPrintf(errlogMajor, errTxt);
|
||||
if(axis!= NULL){
|
||||
if (strstr(myReply, "?cmd") != NULL)
|
||||
{
|
||||
snprintf(errTxt, sizeof(errTxt), "Bad command %s at axis %d", command, axisNo);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, errTxt);
|
||||
if (axis != NULL)
|
||||
{
|
||||
axis->updateMsgTxtFromDriver(errTxt);
|
||||
}
|
||||
return asynError;
|
||||
} else if(strstr(myReply,"?par") != NULL){
|
||||
snprintf(errTxt,sizeof(errTxt), "Bad parameter in command %s", command);
|
||||
errlogSevPrintf(errlogMajor, errTxt);
|
||||
if(axis!= NULL){
|
||||
}
|
||||
else if (strstr(myReply, "?par") != NULL)
|
||||
{
|
||||
snprintf(errTxt, sizeof(errTxt), "Bad parameter in command %s", command);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, errTxt);
|
||||
if (axis != NULL)
|
||||
{
|
||||
axis->updateMsgTxtFromDriver(errTxt);
|
||||
}
|
||||
return asynError;
|
||||
} else if(strstr(myReply,"?rng") != NULL){
|
||||
snprintf(errTxt,sizeof(errTxt), "Parameter out of range in command %s", command);
|
||||
errlogSevPrintf(errlogMajor, errTxt);
|
||||
if(axis!= NULL){
|
||||
}
|
||||
else if (strstr(myReply, "?rng") != NULL)
|
||||
{
|
||||
snprintf(errTxt, sizeof(errTxt), "Parameter out of range in command %s", command);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, errTxt);
|
||||
if (axis != NULL)
|
||||
{
|
||||
axis->updateMsgTxtFromDriver(errTxt);
|
||||
}
|
||||
return asynError;
|
||||
@ -210,51 +218,99 @@ asynStatus EL734Controller::transactController(int axisNo,char command[COMLEN],
|
||||
// These are the EL734Axis methods
|
||||
|
||||
/** Creates a new EL734Axis object.
|
||||
* \param[in] pC Pointer to the EL734Controller to which this axis belongs.
|
||||
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
|
||||
*
|
||||
* Initializes register numbers, etc.
|
||||
*/
|
||||
* \param[in] pC Pointer to the EL734Controller to which this axis belongs.
|
||||
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
|
||||
*
|
||||
* Initializes register numbers, etc.
|
||||
*/
|
||||
EL734Axis::EL734Axis(EL734Controller *pC, int axisNo)
|
||||
: SINQAxis(pC, axisNo), pC_(pC)
|
||||
{
|
||||
: SINQAxis(pC, axisNo), pC_(pC)
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
asynStatus status;
|
||||
int count = 0;
|
||||
float low, high;
|
||||
|
||||
/*
|
||||
get the hardware limits from the controller
|
||||
*/
|
||||
sprintf(command, "H %d", axisNo_);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
if (status == asynSuccess)
|
||||
{
|
||||
count = sscanf(reply, "%f %f", &low, &high);
|
||||
if (count >= 2)
|
||||
{
|
||||
pC_->setDoubleParam(axisNo_, pC_->motorLowLimit_, low);
|
||||
pC_->setDoubleParam(axisNo_, pC_->motorHighLimit_, high);
|
||||
callParamCallbacks();
|
||||
}
|
||||
else
|
||||
{
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Bad response - %s - requesting limits at axis %d", reply, axisNo_);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Failed to read limits at axis %d", axisNo_);
|
||||
}
|
||||
|
||||
/*
|
||||
oredMSR contains the axis status from the previous poll. The initial value
|
||||
1 means that the axis is not moving and has no errors.
|
||||
*/
|
||||
oredMSR = 1;
|
||||
next_poll = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Reports on status of the axis
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* After printing device-specific information calls asynMotorAxis::report()
|
||||
*/
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* After printing device-specific information calls asynMotorAxis::report()
|
||||
*/
|
||||
void EL734Axis::report(FILE *fp, int level)
|
||||
{
|
||||
if (level > 0) {
|
||||
if (level > 0)
|
||||
{
|
||||
fprintf(fp, " axis %d\n",
|
||||
axisNo_);
|
||||
}
|
||||
|
||||
// Call the base class method
|
||||
//asynMotorAxis::report(fp, level);
|
||||
// asynMotorAxis::report(fp, level);
|
||||
}
|
||||
|
||||
|
||||
asynStatus EL734Axis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::move";
|
||||
// static const char *functionName = "EL734Axis::move";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
// status = sendAccelAndVelocity(acceleration, maxVelocity);
|
||||
|
||||
if (relative) {
|
||||
|
||||
errlogPrintf("minVelocity = %f, maxVelocity = %f\n", minVelocity, maxVelocity);
|
||||
|
||||
/*
|
||||
* set speed
|
||||
*/
|
||||
sprintf(command, "J %d %d", axisNo_, (int)maxVelocity);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
|
||||
if (relative)
|
||||
{
|
||||
position += this->position;
|
||||
}
|
||||
oredMSR = 0;
|
||||
homing = 0;
|
||||
sprintf(command, "p %d %.3f", axisNo_, position/1000.);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
errorReported = 0;
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Starting axis %d with destination %f", axisNo_, position / 1000);
|
||||
sprintf(command, "p %d %.3f", axisNo_, position / 1000.);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
updateMsgTxtFromDriver("");
|
||||
next_poll = -1;
|
||||
return status;
|
||||
}
|
||||
@ -262,51 +318,66 @@ asynStatus EL734Axis::move(double position, int relative, double minVelocity, do
|
||||
asynStatus EL734Axis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::home";
|
||||
// static const char *functionName = "EL734Axis::home";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
// status = sendAccelAndVelocity(acceleration, maxVelocity);
|
||||
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
updateMsgTxtFromDriver("");
|
||||
errorReported = 0;
|
||||
|
||||
sprintf(command, "R %d", axisNo_);
|
||||
homing = 1;
|
||||
next_poll= -1;
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
next_poll = -1;
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EL734Axis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::moveVelocity";
|
||||
// static const char *functionName = "EL734Axis::moveVelocity";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
// asynPrint(pasynUser_, ASYN_TRACE_FLOW,
|
||||
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
|
||||
// functionName, minVelocity, maxVelocity, acceleration);
|
||||
|
||||
|
||||
if (maxVelocity > 0.) {
|
||||
errorReported = 0;
|
||||
if (maxVelocity > 0.)
|
||||
{
|
||||
/* This is a positive move */
|
||||
sprintf(command, "FF %d", axisNo_);
|
||||
} else {
|
||||
/* This is a negative move */
|
||||
sprintf(command, "FB %d", axisNo_);
|
||||
}
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
else
|
||||
{
|
||||
/* This is a negative move */
|
||||
sprintf(command, "FB %d", axisNo_);
|
||||
}
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
updateMsgTxtFromDriver("");
|
||||
next_poll = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EL734Axis::stop(double acceleration )
|
||||
asynStatus EL734Axis::stop(double acceleration)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::stop";
|
||||
asynStatus status = asynSuccess;
|
||||
// static const char *functionName = "EL734Axis::stop";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
bool moving = false;
|
||||
|
||||
sprintf(command, "S %d", axisNo_);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Axis interrupted");
|
||||
this->poll(&moving);
|
||||
if (moving && errorReported == 0)
|
||||
{
|
||||
sprintf(command, "S %d", axisNo_);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "Sent STOP on Axis %d\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Axis interrupted");
|
||||
errorReported = 1;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -314,11 +385,11 @@ asynStatus EL734Axis::stop(double acceleration )
|
||||
asynStatus EL734Axis::setPosition(double position)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EL734Axis::setPosition";
|
||||
// static const char *functionName = "EL734Axis::setPosition";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
sprintf(command, "P %d %f", axisNo_, position/1000.);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
sprintf(command, "U %d %f", axisNo_, position / 1000.);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
@ -326,8 +397,8 @@ asynStatus EL734Axis::setPosition(double position)
|
||||
|
||||
asynStatus EL734Axis::setClosedLoop(bool closedLoop)
|
||||
{
|
||||
//static const char *functionName = "EL734Axis::setClosedLoop";
|
||||
|
||||
// static const char *functionName = "EL734Axis::setClosedLoop";
|
||||
|
||||
/*
|
||||
This belongs into the Kingdom of Electronics.
|
||||
We do not do this.
|
||||
@ -337,126 +408,188 @@ asynStatus EL734Axis::setClosedLoop(bool closedLoop)
|
||||
}
|
||||
|
||||
/** Polls the axis.
|
||||
* This function reads the motor position, the limit status, the home status, the moving status,
|
||||
* and the drive power-on status.
|
||||
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
|
||||
* and then calls callParamCallbacks() at the end.
|
||||
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
|
||||
* This function reads the motor position, the limit status, the home status, the moving status,
|
||||
* and the drive power-on status.
|
||||
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
|
||||
* and then calls callParamCallbacks() at the end.
|
||||
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
|
||||
asynStatus EL734Axis::poll(bool *moving)
|
||||
{
|
||||
{
|
||||
int msr, count;
|
||||
asynStatus comStatus;
|
||||
float low, high;
|
||||
asynStatus comStatus = asynSuccess;
|
||||
char command[COMLEN], reply[COMLEN], errTxt[256];
|
||||
int driverError = 0;
|
||||
|
||||
|
||||
// protect against excessive polling
|
||||
if(time(NULL) < next_poll){
|
||||
if (time(NULL) < next_poll)
|
||||
{
|
||||
*moving = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// Read the current motor position
|
||||
sprintf(command,"u %d", axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError){
|
||||
driverError = 1;
|
||||
// read hardware limits
|
||||
sprintf(command, "H %d", axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_, command, reply);
|
||||
if (comStatus == asynSuccess)
|
||||
{
|
||||
count = sscanf(reply, "%f %f", &low, &high);
|
||||
if (count >= 2)
|
||||
{
|
||||
pC_->setDoubleParam(axisNo_, pC_->motorLowLimit_, low);
|
||||
pC_->setDoubleParam(axisNo_, pC_->motorHighLimit_, high);
|
||||
callParamCallbacks();
|
||||
}
|
||||
}
|
||||
if(comStatus) goto skip;
|
||||
if(strstr(reply,"*ES") != NULL){
|
||||
|
||||
// Read the current motor position
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
sprintf(command, "u %d", axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_, command, reply);
|
||||
if (comStatus == asynError)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
goto skip;
|
||||
}
|
||||
if (strstr(reply, "*ES") != NULL)
|
||||
{
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errorReported = 1;
|
||||
updateMsgTxtFromDriver("Emergency Stop Engaged");
|
||||
driverError = 1;
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
} else if(strstr(reply,"?BSY") != NULL){
|
||||
}
|
||||
else if (strstr(reply, "?BSY") != NULL)
|
||||
{
|
||||
*moving = true;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
updateMsgTxtFromDriver(NULL);
|
||||
goto skip;
|
||||
}
|
||||
count = sscanf(reply,"%lf", &position);
|
||||
if(count != 1) {
|
||||
snprintf(errTxt,sizeof(errTxt),"Bad reply %s when reading position for %d", reply, axisNo_);
|
||||
updateMsgTxtFromDriver(errTxt);
|
||||
comStatus = asynError;
|
||||
driverError =1;
|
||||
goto skip;
|
||||
count = sscanf(reply, "%lf", &position);
|
||||
if (count != 1)
|
||||
{
|
||||
if (!homing)
|
||||
{
|
||||
snprintf(errTxt, sizeof(errTxt), "Bad reply %s when reading position for %d", reply, axisNo_);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errorReported = 1;
|
||||
updateMsgTxtFromDriver(errTxt);
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Axis %d, reply %s, position %lf\n", axisNo_, reply, position);
|
||||
setDoubleParam(pC_->motorPosition_, position * 1000);
|
||||
// setDoubleParam(pC_->motorEncoderPosition_, position);
|
||||
}
|
||||
//errlogPrintf("Axis %d, reply %s, position %lf\n", axisNo_, reply, position);
|
||||
setDoubleParam(pC_->motorPosition_, position*1000);
|
||||
//setDoubleParam(pC_->motorEncoderPosition_, position);
|
||||
|
||||
|
||||
// Read the moving status of this motor
|
||||
sprintf(command,"msr %d",axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus) goto skip;
|
||||
sscanf(reply,"%x",&msr);
|
||||
// errlogPrintf("Axis %d, reply %s, msr %d, position = %lf\n",
|
||||
// axisNo_, reply, msr, position);
|
||||
sprintf(command, "msr %d", axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_, command, reply);
|
||||
if (comStatus == asynError)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
goto skip;
|
||||
}
|
||||
sscanf(reply, "%x", &msr);
|
||||
|
||||
// errlogPrintf("Axis %d, reply %s, msr %d, oredmsr = %d, position = %lf\n",
|
||||
// axisNo_, reply, msr, oredMSR, position);
|
||||
|
||||
oredMSR |= msr;
|
||||
if( (msr & 0x1) == 0){
|
||||
if ((msr & 0x1) == 0)
|
||||
{
|
||||
// done: check for trouble
|
||||
//errlogPrintf("Axis %d finished\n", axisNo_);
|
||||
next_poll = time(NULL)+IDLEPOLL;
|
||||
if(oredMSR & 0x10){
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
updateMsgTxtFromDriver("Lower Limit Hit");
|
||||
driverError = 1;
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, false);
|
||||
}
|
||||
if(oredMSR & 0x20){
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
updateMsgTxtFromDriver("Upper Limit Hit");
|
||||
driverError = 1;
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, false);
|
||||
}
|
||||
if(homing){
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
}
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
if(oredMSR &0x1000){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Air cushion problem on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Air cushion error");
|
||||
driverError = 1;
|
||||
}
|
||||
if(oredMSR &0x80){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Positioning fault at %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Positioning fault");
|
||||
driverError = 1;
|
||||
}
|
||||
// errlogPrintf("Axis %d finished\n", axisNo_);
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
//updateMsgTxtFromDriver("Believed to be on position");
|
||||
} else {
|
||||
|
||||
next_poll = time(NULL) + IDLEPOLL;
|
||||
if (oredMSR & 0x10)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
updateMsgTxtFromDriver("Lower Limit Hit");
|
||||
errorReported = 1;
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
}
|
||||
else
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, false);
|
||||
}
|
||||
if (oredMSR & 0x20)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
updateMsgTxtFromDriver("Upper Limit Hit");
|
||||
errorReported = 1;
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
}
|
||||
else
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, false);
|
||||
}
|
||||
if (homing)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
}
|
||||
if (oredMSR & 0x1000)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
// errlogPrintf("Detected air cushion error on %d", axisNo_);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "Air cushion problem on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Air cushion error");
|
||||
errorReported = 1;
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
}
|
||||
if (oredMSR & 0x100)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "Run failure at %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Run failure");
|
||||
comStatus = asynError;
|
||||
errorReported = 1;
|
||||
goto skip;
|
||||
}
|
||||
if (oredMSR & 0x400)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "Positioning failure at %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Positioning failure");
|
||||
comStatus = asynError;
|
||||
errorReported = 1;
|
||||
goto skip;
|
||||
}
|
||||
if (oredMSR & 0x200 || oredMSR & 0x80)
|
||||
{
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING, "Positioning fault at %d", axisNo_);
|
||||
}
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
*moving = true;
|
||||
next_poll = -1;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
//updateMsgTxtFromDriver("Creeping");
|
||||
}
|
||||
|
||||
skip:
|
||||
if(driverError == 0){
|
||||
updateMsgTxtFromDriver(NULL);
|
||||
}
|
||||
setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0);
|
||||
skip:
|
||||
callParamCallbacks();
|
||||
return comStatus ? asynError : asynSuccess;
|
||||
return comStatus;
|
||||
}
|
||||
|
||||
/** Code for iocsh registration */
|
||||
static const iocshArg EL734CreateControllerArg0 = {"Port name", iocshArgString};
|
||||
static const iocshArg EL734CreateControllerArg1 = {"EL734 port name", iocshArgString};
|
||||
static const iocshArg EL734CreateControllerArg2 = {"Number of axes", iocshArgInt};
|
||||
static const iocshArg * const EL734CreateControllerArgs[] = {&EL734CreateControllerArg0,
|
||||
&EL734CreateControllerArg1,
|
||||
&EL734CreateControllerArg2};
|
||||
static const iocshArg *const EL734CreateControllerArgs[] = {&EL734CreateControllerArg0,
|
||||
&EL734CreateControllerArg1,
|
||||
&EL734CreateControllerArg2};
|
||||
static const iocshFuncDef EL734CreateControllerDef = {"EL734CreateController", 3, EL734CreateControllerArgs};
|
||||
static void EL734CreateContollerCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
@ -468,6 +601,7 @@ static void EL734Register(void)
|
||||
iocshRegister(&EL734CreateControllerDef, EL734CreateContollerCallFunc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(EL734Register);
|
||||
extern "C"
|
||||
{
|
||||
epicsExportRegistrar(EL734Register);
|
||||
}
|
||||
|
@ -28,33 +28,32 @@ public:
|
||||
asynStatus setClosedLoop(bool closedLoop);
|
||||
|
||||
private:
|
||||
EL734Controller *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
|
||||
* Abbreviated because it is used very frequently */
|
||||
EL734Controller *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
|
||||
* Abbreviated because it is used very frequently */
|
||||
int oredMSR;
|
||||
double position;
|
||||
int homing;
|
||||
time_t next_poll;
|
||||
int ErrTxtIdx;
|
||||
int errorReported;
|
||||
|
||||
void forwardErrorTxt(EL734Axis *axis);
|
||||
|
||||
friend class EL734Controller;
|
||||
friend class EL734Controller;
|
||||
};
|
||||
|
||||
class EL734Controller : public SINQController {
|
||||
class EL734Controller : public SINQController
|
||||
{
|
||||
public:
|
||||
EL734Controller(const char *portName, const char *EL734PortName, int numAxes);
|
||||
|
||||
void report(FILE *fp, int level);
|
||||
EL734Axis* getAxis(asynUser *pasynUser);
|
||||
EL734Axis* getAxis(int axisNo);
|
||||
EL734Axis *getAxis(asynUser *pasynUser);
|
||||
EL734Axis *getAxis(int axisNo);
|
||||
|
||||
friend class EL734Axis;
|
||||
private:
|
||||
asynUser *pasynUserController_;
|
||||
friend class EL734Axis;
|
||||
|
||||
asynStatus transactController(int axis, char command[COMLEN], char reply[COMLEN]);
|
||||
private:
|
||||
asynUser *pasynUserController_;
|
||||
|
||||
void switchRemote();
|
||||
asynStatus transactController(int axis, char command[COMLEN], char reply[COMLEN]);
|
||||
|
||||
void switchRemote();
|
||||
};
|
||||
|
679
sinqEPICSApp/src/EuroMoveDriver.cpp
Normal file
679
sinqEPICSApp/src/EuroMoveDriver.cpp
Normal file
@ -0,0 +1,679 @@
|
||||
/*
|
||||
FILENAME... EuroMoveDriver.cpp
|
||||
USAGE... Motor driver support for the EuroMove motor controller from LLB.
|
||||
|
||||
This is a driver for the EuroMove motor controller from LLB. This device talks to us either via
|
||||
GPIB or USB. The controller is fairly simple. It can control quite a number of motors.
|
||||
|
||||
Reworked in 06/2021 to work with the Prologix GPIV-Ethernet converter
|
||||
|
||||
mark.koennecke@psi.ch
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iocsh.h>
|
||||
#include <epicsThread.h>
|
||||
#include <errlog.h>
|
||||
|
||||
#include <asynOctetSyncIO.h>
|
||||
|
||||
#include "EuroMoveDriver.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
#define IDLEPOLL 60
|
||||
|
||||
/** Creates a new EuroMoveController object.
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] EuroMovePortName The name of the drvAsynSerialPort that was created previously to connect to the EuroMove controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
EuroMoveController::EuroMoveController(const char *portName, const char *EuroMovePortName, int gpibAddr, int numAxis)
|
||||
: SINQController(portName, EuroMovePortName,numAxis+1)
|
||||
{
|
||||
asynStatus status;
|
||||
static const char *functionName = "EuroMoveController::EuroMoveController";
|
||||
size_t out, in;
|
||||
int reason;
|
||||
char command[80], reply[80];
|
||||
|
||||
/* Connect to EuroMove controller */
|
||||
status = pasynOctetSyncIO->connect(EuroMovePortName, 0, &pasynUserController_, NULL);
|
||||
if (status) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: cannot connect to EuroMove controller\n",
|
||||
functionName);
|
||||
}
|
||||
const char *terminator = "\r\n";
|
||||
pasynOctetSyncIO->setOutputEos(pasynUserController_,terminator,strlen(terminator));
|
||||
const char *repTerminator = "\r";
|
||||
pasynOctetSyncIO->setInputEos(pasynUserController_,repTerminator,strlen(repTerminator));
|
||||
|
||||
/* Save gpib address and prepare addr string */
|
||||
this->gpibAddr = gpibAddr;
|
||||
snprintf(this->addrCommand,sizeof(addrCommand), "++addr %d", gpibAddr);
|
||||
|
||||
/*
|
||||
Configure the Prologix interface to our liking
|
||||
The thing sends no response on configuration commands
|
||||
*/
|
||||
strcpy(command,"++mode 1"); /* Make it a controller */
|
||||
status = pasynOctetSyncIO->write(pasynUserController_, command, strlen(command), 2, &out);
|
||||
strcpy(command,"++auto 1"); /* ask for response automatically */
|
||||
status = pasynOctetSyncIO->write(pasynUserController_, command, strlen(command), 2, &out);
|
||||
strcpy(command,"++eos 1"); /* CR */
|
||||
status = pasynOctetSyncIO->write(pasynUserController_, command, strlen(command), 2, &out);
|
||||
|
||||
|
||||
|
||||
for(int i = 0; i < numAxis; i++){
|
||||
new EuroMoveAxis(this, i+1);
|
||||
}
|
||||
startPoller(1000./1000., IDLEPOLL, 2);
|
||||
}
|
||||
|
||||
|
||||
/** Creates a new EuroMoveController object.
|
||||
* Configuration command, called directly or from iocsh
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] EuroMovePortName The name of the drvAsynIPPPort that was created previously to connect to the EuroMove controller
|
||||
* \param[in] gpibAddr The address of the controller on the GPIB bus
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
extern "C" int EuroMoveCreateController(const char *portName, const char *EuroMovePortName, const int gpibAddr, const int numAxis)
|
||||
{
|
||||
new EuroMoveController(portName, EuroMovePortName, gpibAddr, numAxis);
|
||||
return(asynSuccess);
|
||||
}
|
||||
|
||||
/** Reports on status of the driver
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* If details > 0 then information is printed about each axis.
|
||||
* After printing controller-specific information it calls asynMotorController::report()
|
||||
*/
|
||||
void EuroMoveController::report(FILE *fp, int level)
|
||||
{
|
||||
fprintf(fp, "EuroMove motor driver %s, numAxes=%d\n",
|
||||
this->portName, numAxes_);
|
||||
|
||||
// Call the base class method
|
||||
asynMotorController::report(fp, level);
|
||||
}
|
||||
|
||||
/** Returns a pointer to an EuroMoveAxis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
|
||||
EuroMoveAxis* EuroMoveController::getAxis(asynUser *pasynUser)
|
||||
{
|
||||
return static_cast<EuroMoveAxis*>(asynMotorController::getAxis(pasynUser));
|
||||
}
|
||||
|
||||
/** Returns a pointer to an EuroMoveAxis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] axisNo Axis index number. */
|
||||
EuroMoveAxis* EuroMoveController::getAxis(int axisNo)
|
||||
{
|
||||
return static_cast<EuroMoveAxis*>(asynMotorController::getAxis(axisNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* send a command to the EuroMove and read the reply. Do test for errors.
|
||||
* \param[in] command The command to send
|
||||
* \param[out] reply The controllers reply
|
||||
*/
|
||||
|
||||
asynStatus EuroMoveController::transactController(int axisNo,char command[COMLEN], char reply[COMLEN])
|
||||
{
|
||||
asynStatus status;
|
||||
SINQAxis *axis = getAxis(axisNo);
|
||||
size_t out, in;
|
||||
int reason, errCode, lCode;
|
||||
char pDummy[50], lReply[50];
|
||||
const char *lCommand = {"L"};
|
||||
|
||||
|
||||
|
||||
pasynOctetSyncIO->flush(pasynUserController_);
|
||||
|
||||
/* set address first */
|
||||
status = pasynOctetSyncIO->write(pasynUserController_, this->addrCommand, strlen(this->addrCommand), 2, &out);
|
||||
if(status != asynSuccess){
|
||||
if(axis!= NULL){
|
||||
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
|
||||
errlogPrintf("Lost connection to motor controller\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* now the actual command */
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,sizeof(reply), 10.,&out,&in,&reason);
|
||||
if(status != asynSuccess){
|
||||
if(axis!= NULL){
|
||||
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
|
||||
errlogPrintf("Lost connection to motor controller\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if(strstr(reply,"ERROR") != NULL){
|
||||
sscanf((const char *)reply, "%s %d", pDummy, &errCode);
|
||||
switch(errCode){
|
||||
case 0:
|
||||
axis->updateMsgTxtFromDriver("Syntax error or parameter out of range");
|
||||
errlogPrintf("Syntax error or parameter out or range on %d\n", axisNo);
|
||||
break;
|
||||
case 1:
|
||||
axis->updateMsgTxtFromDriver("Timeout communicating with daughter board");
|
||||
errlogPrintf("Timeout communicating with daughter board on %d\n", axisNo);
|
||||
break;
|
||||
case 2:
|
||||
axis->updateMsgTxtFromDriver("This is not a drivable motor");
|
||||
errlogPrintf("This is not a drivable motor on %d\n", axisNo);
|
||||
break;
|
||||
case 3:
|
||||
axis->updateMsgTxtFromDriver("Encoder anomaly");
|
||||
errlogPrintf("Encoder anomaly on %d\n", axisNo);
|
||||
break;
|
||||
case 4:
|
||||
axis->updateMsgTxtFromDriver("Attempt to move across limit switch");
|
||||
errlogPrintf("Attempt to move across limit switch on %d\n", axisNo);
|
||||
break;
|
||||
case 5:
|
||||
axis->updateMsgTxtFromDriver("Badly configured relay system");
|
||||
errlogPrintf("Badly configured relay system on %d\n", axisNo);
|
||||
break;
|
||||
case 6:
|
||||
axis->updateMsgTxtFromDriver("Non valid backup");
|
||||
errlogPrintf("Invalid backup on %d\n", axisNo);
|
||||
break;
|
||||
case 7:
|
||||
axis->updateMsgTxtFromDriver("Backup failed");
|
||||
errlogPrintf("Backup failed on %d\n", axisNo);
|
||||
break;
|
||||
}
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/*
|
||||
Test system status
|
||||
*/
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, lCommand, strlen(lCommand),
|
||||
lReply,sizeof(lReply), 1.,&out,&in,&reason);
|
||||
if(status != asynSuccess){
|
||||
if(axis!= NULL){
|
||||
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
|
||||
errlogPrintf("Lost connection to motor controller\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
sscanf(lReply, "%x", &lCode);
|
||||
//errlogPrintf("System status returned %s, converted to %d\n", lReply, lCode);
|
||||
if((lCode & 1) > 0){
|
||||
axis->updateMsgTxtFromDriver("Syntax error or impossible to execute");
|
||||
errlogPrintf("Syntax error or impossible to execute for %s on %d\n", command, axisNo);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
asynStatus EuroMoveController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
|
||||
{
|
||||
int function = pasynUser->reason;
|
||||
asynStatus status = asynError;
|
||||
EuroMoveAxis *pAxis = NULL;
|
||||
|
||||
static const char *functionName = "EuroMoveController::writeFloat64";
|
||||
|
||||
|
||||
pAxis = (EuroMoveAxis *)this->getAxis(pasynUser);
|
||||
if (!pAxis) {
|
||||
return asynError;
|
||||
}
|
||||
|
||||
|
||||
/* Set the parameter and readback in the parameter library. */
|
||||
status = pAxis->setDoubleParam(function, value);
|
||||
|
||||
errlogPrintf("%s, reason %d, value %f\n", functionName, function, value);
|
||||
|
||||
// TODO: somethign is really shitty here: lowLimit and highLimit cannot be on the command
|
||||
if (function == motorResolution_) {
|
||||
if(value > .0){
|
||||
pAxis->resolution = (int)(1./value);
|
||||
} else {
|
||||
pAxis->resolution = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Call base class method
|
||||
//This will handle callCallbacks even if the function was handled here.
|
||||
status = asynMotorController::writeFloat64(pasynUser, value);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
// These are the EuroMoveAxis methods
|
||||
|
||||
/** Creates a new EuroMoveAxis object.
|
||||
* \param[in] pC Pointer to the EuroMoveController to which this axis belongs.
|
||||
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
|
||||
*
|
||||
* Initializes register numbers, etc.
|
||||
*/
|
||||
EuroMoveAxis::EuroMoveAxis(EuroMoveController *pC, int axisNo)
|
||||
: SINQAxis(pC, axisNo),
|
||||
pC_(pC)
|
||||
{
|
||||
motNo = axisNo;
|
||||
next_poll = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Reports on status of the axis
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* After printing device-specific information calls asynMotorAxis::report()
|
||||
*/
|
||||
void EuroMoveAxis::report(FILE *fp, int level)
|
||||
{
|
||||
if (level > 0) {
|
||||
fprintf(fp, " axis %d\n",
|
||||
axisNo_);
|
||||
}
|
||||
asynMotorAxis::report(fp, level);
|
||||
}
|
||||
|
||||
|
||||
asynStatus EuroMoveAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EuroMoveAxis::move";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
if (relative) {
|
||||
position += this->position;
|
||||
}
|
||||
homingStatus = HomeIdle;
|
||||
sprintf(command,"G%d=%d", motNo, (int)position);
|
||||
status = pC_->transactController(motNo,command,reply);
|
||||
next_poll = -1;
|
||||
targetPosition = (int)position;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EuroMoveAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
|
||||
{
|
||||
asynStatus status = asynSuccess;
|
||||
//static const char *functionName = "EuroMoveAxis::home";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
/*
|
||||
The EuroMove controller does not have a built in homing mechanism. We simulate it by:
|
||||
|
||||
1). Running the motor in the appropriate end swicth at high speed
|
||||
2) Stepping back three units
|
||||
3) Run the motor again in the end switch at low speed for better accuracy
|
||||
4) Set the limit position with the <I> command
|
||||
|
||||
The state of this operation is maintained in the homingStatus variable
|
||||
*/
|
||||
homingDirection = forwards;
|
||||
homingStatus = HomeFastRun;
|
||||
if(homingDirection){
|
||||
sprintf(command,"H%d=03", motNo);
|
||||
} else {
|
||||
sprintf(command,"H%d=07", motNo);
|
||||
}
|
||||
|
||||
status = pC_->transactController(motNo,command,reply);
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EuroMoveAxis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status;
|
||||
double target;
|
||||
|
||||
//static const char *functionName = "EuroMoveAxis::moveVelocity";
|
||||
|
||||
// asynPrint(pasynUser_, ASYN_TRACE_FLOW,
|
||||
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
|
||||
// functionName, minVelocity, maxVelocity, acceleration);
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
if (maxVelocity > 0.) {
|
||||
/* This is a positive move */
|
||||
pC_->getDoubleParam(axisNo_,pC_->motorHighLimit_,&target);
|
||||
} else {
|
||||
/* This is a negative move */
|
||||
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&target);
|
||||
}
|
||||
status = move(target,0,0,0,0);
|
||||
next_poll = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EuroMoveAxis::sendStop()
|
||||
{
|
||||
asynStatus status;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
sprintf(command, "B%d", motNo);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EuroMoveAxis::stop(double acceleration )
|
||||
{
|
||||
asynStatus status;
|
||||
// static const char *functionName = "EuroMoveAxis::stop";
|
||||
|
||||
status = sendStop();
|
||||
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Axis interrupted");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EuroMoveAxis::setPosition(double position)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "EuroMoveAxis::setPosition";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
errlogPrintf("EuroMoveAxis::setPosition called with %lf\n", position);
|
||||
|
||||
sprintf(command, "I%d=%d",motNo,(int)position);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus EuroMoveAxis::setClosedLoop(bool closedLoop)
|
||||
{
|
||||
//static const char *functionName = "EuroMoveAxis::setClosedLoop";
|
||||
|
||||
/*
|
||||
This belongs into the Kingdom of Electronics.
|
||||
We do not do this.
|
||||
*/
|
||||
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/** Polls the axis.
|
||||
* This function reads the motor position, the limit status, the home status, the moving status,
|
||||
* and the drive power-on status.
|
||||
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
|
||||
* and then calls callParamCallbacks() at the end.
|
||||
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
|
||||
asynStatus EuroMoveAxis::poll(bool *moving)
|
||||
{
|
||||
asynStatus comStatus = asynSuccess;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
unsigned int axStatus;
|
||||
double backStep, backPos, limit;
|
||||
|
||||
// protect against excessive polling
|
||||
if(time(NULL) < next_poll){
|
||||
*moving = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
|
||||
/*
|
||||
read the current position
|
||||
*/
|
||||
sprintf(command,"A%d",motNo);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError) {
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
goto skip;
|
||||
}
|
||||
sscanf(reply,"%d",&position);
|
||||
setDoubleParam(pC_->motorPosition_,(double)position);
|
||||
|
||||
// Read the moving status of this motor
|
||||
sprintf(command,"E%d",motNo);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/* errlogPrintf("Axis %d, status reply %s, position %d\n", axisNo_, reply, position); */
|
||||
sscanf(reply, "%x", &axStatus);
|
||||
|
||||
if(homingStatus == HomeIdle){
|
||||
if((axStatus & 128) > 0) { // test bit 8
|
||||
*moving = true;
|
||||
next_poll = -1;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
} else {
|
||||
*moving = false;
|
||||
next_poll = time(NULL)+IDLEPOLL;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
}
|
||||
|
||||
/* Testing limit switches */
|
||||
if((axStatus & 2) > 0){ // bit 2
|
||||
if(targetPosition > position) {
|
||||
/*
|
||||
Error codition only when we wish to move further
|
||||
*/
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
errlogPrintf("HighLimit detected on %d\n", motNo);
|
||||
updateMsgTxtFromDriver("On high limit switch");
|
||||
sendStop();
|
||||
}
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, false);
|
||||
}
|
||||
if((axStatus & 1) > 0){ // bit 1
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
if(targetPosition < position) {
|
||||
/*
|
||||
Error treatment onlly when we want to go below the limits
|
||||
*/
|
||||
errlogPrintf("LowLimit detected on %d\n", motNo);
|
||||
sendStop();
|
||||
updateMsgTxtFromDriver("On low limit switch");
|
||||
}
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, false);
|
||||
}
|
||||
|
||||
/* there could be errors reported in the motor status */
|
||||
if((axStatus & 8) > 0){ // bit 4
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("Internal timeout detected");
|
||||
errlogPrintf("Internal timeout detected on %d\n", motNo);
|
||||
comStatus = asynError;
|
||||
}
|
||||
if((axStatus & 4) > 0){ // bit 3
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("Encoding anomaly");
|
||||
errlogPrintf("Encoding anomaly on %d\n", motNo);
|
||||
comStatus = asynError;
|
||||
}
|
||||
} else {
|
||||
next_poll = -1;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
*moving = true;
|
||||
switch(homingStatus){
|
||||
case HomeIdle:
|
||||
// handled above: this is here to silence the compiler
|
||||
break;
|
||||
case HomeFastRun:
|
||||
// errlogPrintf("HomeFastRun\n");
|
||||
if(axStatus & 2 || axStatus & 1){
|
||||
sendStop();
|
||||
homingStatus = HomeFastStop;
|
||||
}
|
||||
break;
|
||||
case HomeFastStop:
|
||||
// errlogPrintf("HomeFastStop\n");
|
||||
if((axStatus & 128) == 0){
|
||||
backStep = 3 * resolution;
|
||||
if(homingDirection){
|
||||
backPos = position - backStep;
|
||||
} else {
|
||||
backPos = position + backStep;
|
||||
}
|
||||
sprintf(command,"G%d=%d", motNo, (int)backPos);
|
||||
comStatus = pC_->transactController(motNo,command,reply);
|
||||
if(comStatus == asynError){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("Homing problem when back stepping");
|
||||
errlogPrintf("Homing problem when back stepping: %s not accepted\n", command);
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
homingStatus = HomeIdle;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
goto skip;
|
||||
}
|
||||
usleep(200);
|
||||
homingStatus = HomeBackStep;
|
||||
}
|
||||
break;
|
||||
case HomeBackStep:
|
||||
//errlogPrintf("HomeBackStep\n");
|
||||
if((axStatus & 128) == 0){
|
||||
homingStatus = HomeSlowRun;
|
||||
if(homingDirection){
|
||||
sprintf(command,"H%d=01", motNo);
|
||||
} else {
|
||||
sprintf(command,"H%d=05", motNo);
|
||||
}
|
||||
comStatus = pC_->transactController(motNo,command,reply);
|
||||
if(comStatus == asynError){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("Homing problem slow running");
|
||||
errlogPrintf("Homing problem when slow running: %s not accepted\n", command);
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
homingStatus = HomeIdle;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
goto skip;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HomeSlowRun:
|
||||
//errlogPrintf("HomeSlowRun\n");
|
||||
if(axStatus & 2 || axStatus & 1){
|
||||
sendStop();
|
||||
homingStatus = HomeSlowStop;
|
||||
}
|
||||
break;
|
||||
case HomeSlowStop:
|
||||
//errlogPrintf("HomeSlowStop\n");
|
||||
if((axStatus & 128) == 0) {
|
||||
if(homingDirection) {
|
||||
pC_->getDoubleParam(axisNo_,pC_->motorHighLimit_,&limit);
|
||||
} else {
|
||||
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&limit);
|
||||
}
|
||||
setPosition(limit);
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
next_poll = time(NULL)+IDLEPOLL;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
homingStatus = HomeIdle;
|
||||
setDoubleParam(pC_->motorPosition_,(double)limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip:
|
||||
callParamCallbacks();
|
||||
return comStatus;
|
||||
}
|
||||
|
||||
/** Code for configuring the motNo on axis **/
|
||||
extern "C" {
|
||||
asynStatus EuroMoveConfigureAxis(const char *euroMoveName, unsigned int axisNo, unsigned int motNo)
|
||||
{
|
||||
EuroMoveController *pC = NULL;
|
||||
EuroMoveAxis *pAxis = NULL;
|
||||
|
||||
pC = (EuroMoveController *)findAsynPortDriver(euroMoveName);
|
||||
if(!pC){
|
||||
errlogPrintf("%s driver not found", euroMoveName);
|
||||
return asynError;
|
||||
}
|
||||
pC->unlock();
|
||||
|
||||
pAxis = (EuroMoveAxis *)pC->getAxis(axisNo);
|
||||
if(!pAxis){
|
||||
errlogPrintf("axis %d driver not found on %s", axisNo, euroMoveName);
|
||||
return asynError;
|
||||
}
|
||||
pAxis->setMotNo(motNo);
|
||||
|
||||
return asynSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
static const iocshArg EuroMoveConfigArg0 = {"Port name", iocshArgString};
|
||||
static const iocshArg EuroMoveConfigArg1 = {"Axis Index", iocshArgInt};
|
||||
static const iocshArg EuroMoveConfigArg2 = {"Motor number in EuroMove", iocshArgInt};
|
||||
static const iocshArg * const EuroMoveConfigArgs[] = {&EuroMoveConfigArg0,
|
||||
&EuroMoveConfigArg1,
|
||||
&EuroMoveConfigArg2
|
||||
};
|
||||
static const iocshFuncDef EuroMoveConfigDef = {"EuroMoveConfigureAxis", 3, EuroMoveConfigArgs};
|
||||
static void EuroMoveConfigCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
EuroMoveConfigureAxis(args[0].sval, (unsigned int)args[1].ival, (unsigned int)args[2].ival);
|
||||
}
|
||||
|
||||
|
||||
/** Code for iocsh registration */
|
||||
static const iocshArg EuroMoveCreateControllerArg0 = {"Port name", iocshArgString};
|
||||
static const iocshArg EuroMoveCreateControllerArg1 = {"EuroMove port name", iocshArgString};
|
||||
static const iocshArg EuroMoveCreateControllerArg2 = {"GPIB Address", iocshArgInt};
|
||||
static const iocshArg EuroMoveCreateControllerArg3 = {"Number of axis", iocshArgInt};
|
||||
static const iocshArg * const EuroMoveCreateControllerArgs[] = {&EuroMoveCreateControllerArg0,
|
||||
&EuroMoveCreateControllerArg1,
|
||||
&EuroMoveCreateControllerArg2,
|
||||
&EuroMoveCreateControllerArg3
|
||||
};
|
||||
static const iocshFuncDef EuroMoveCreateControllerDef = {"EuroMoveCreateController", 4, EuroMoveCreateControllerArgs};
|
||||
static void EuroMoveCreateControllerCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
EuroMoveCreateController(args[0].sval, args[1].sval, args[2].ival, args[3].ival);
|
||||
}
|
||||
|
||||
static void EuroMoveRegister(void)
|
||||
{
|
||||
iocshRegister(&EuroMoveCreateControllerDef, EuroMoveCreateControllerCallFunc);
|
||||
iocshRegister(&EuroMoveConfigDef, EuroMoveConfigCallFunc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(EuroMoveRegister);
|
||||
}
|
72
sinqEPICSApp/src/EuroMoveDriver.h
Normal file
72
sinqEPICSApp/src/EuroMoveDriver.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
FILENAME... EuroMoveDriver.h
|
||||
USAGE... Motor driver support for the LLB EuroMove controller
|
||||
|
||||
Mark Koennecke
|
||||
January 2020
|
||||
|
||||
*/
|
||||
|
||||
#include "SINQController.h"
|
||||
#include "SINQAxis.h"
|
||||
|
||||
#define COMLEN 80
|
||||
|
||||
typedef enum {
|
||||
HomeIdle,
|
||||
HomeFastRun,
|
||||
HomeFastStop,
|
||||
HomeBackStep,
|
||||
HomeSlowRun,
|
||||
HomeSlowStop,
|
||||
} HomingStatus;
|
||||
|
||||
class EuroMoveAxis : public SINQAxis
|
||||
{
|
||||
public:
|
||||
/* These are the methods we override from the base class */
|
||||
EuroMoveAxis(class EuroMoveController *pC, int axis);
|
||||
void report(FILE *fp, int level);
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
|
||||
asynStatus stop(double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus setPosition(double position);
|
||||
asynStatus setClosedLoop(bool closedLoop);
|
||||
void setMotNo(unsigned int no){
|
||||
motNo = no;
|
||||
}
|
||||
|
||||
private:
|
||||
EuroMoveController *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
|
||||
* Abbreviated because it is used very frequently */
|
||||
time_t next_poll;
|
||||
int motNo; // motor number in the EuroMove controller
|
||||
int position;
|
||||
HomingStatus homingStatus;
|
||||
int homingDirection; // 1 = forward, 0 backwards
|
||||
asynStatus sendStop();
|
||||
int resolution;
|
||||
friend class EuroMoveController;
|
||||
int targetPosition;
|
||||
};
|
||||
|
||||
class EuroMoveController : public SINQController {
|
||||
public:
|
||||
EuroMoveController(const char *portName, const char *EuroMovePortName, const int gpibAddr, const int nAxis);
|
||||
|
||||
void report(FILE *fp, int level);
|
||||
EuroMoveAxis* getAxis(asynUser *pasynUser);
|
||||
EuroMoveAxis* getAxis(int axisNo);
|
||||
asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
|
||||
|
||||
friend class EuroMoveAxis;
|
||||
private:
|
||||
asynUser *pasynUserController_;
|
||||
unsigned int gpibAddr;
|
||||
char addrCommand[50];
|
||||
|
||||
asynStatus transactController(int axisNo, char command[COMLEN], char reply[COMLEN]);
|
||||
|
||||
};
|
@ -28,7 +28,8 @@ sinqEPICS_SRCS += EL734Driver.cpp devScalerEL737.c pmacAsynIPPort.c SINQAxis.cpp
|
||||
sinqEPICS_SRCS += pmacController.cpp pmacAxis.cpp
|
||||
sinqEPICS_SRCS += NanotecDriver.cpp stptok.cpp
|
||||
sinqEPICS_SRCS += PhytronDriver.cpp
|
||||
|
||||
sinqEPICS_SRCS += EuroMoveDriver.cpp
|
||||
sinqEPICS_SRCS += CounterBox.cpp
|
||||
|
||||
# Build the main IOC entry point on workstation OSs.
|
||||
sinqEPICS_SRCS_DEFAULT += sinqEPICSMain.cpp
|
||||
|
954
sinqEPICSApp/src/MasterMACSDriver.cpp
Normal file
954
sinqEPICSApp/src/MasterMACSDriver.cpp
Normal file
@ -0,0 +1,954 @@
|
||||
/*
|
||||
Driver for the MasterMACS motor controller used at SINQ
|
||||
For documentation see the standard SINQ place for hardware documentation or
|
||||
Marcel Schildt
|
||||
|
||||
Mark Koennecke, March-August 2023
|
||||
Mark Koenencke, More fixes in June 2024
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iocsh.h>
|
||||
#include <epicsThread.h>
|
||||
#include <errlog.h>
|
||||
|
||||
#include <asynOctetSyncIO.h>
|
||||
|
||||
#include "MasterMACSDriver.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
#define CHECK_BIT(var,pos) ((var) & (1 << pos))
|
||||
#define ABS(x) (x < 0 ? -(x) : (x))
|
||||
|
||||
#define debug 0
|
||||
#define timeDebug 0
|
||||
|
||||
double DoubleTime(void)
|
||||
{
|
||||
struct timeval now;
|
||||
/* the resolution of this function is usec, if the machine supports this
|
||||
and the mantissa of a double is 51 bits or more (31 bits for seconds
|
||||
and 20 for microseconds)
|
||||
*/
|
||||
gettimeofday(&now, NULL);
|
||||
return now.tv_sec + now.tv_usec / 1e6;
|
||||
}
|
||||
|
||||
/** Creates a new MasterMACSController object.
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] MasterMACSPortName The name of the drvAsynSerialPort that was created previously to connect to the MasterMACS controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
MasterMACSController::MasterMACSController(const char *portName,
|
||||
const char *MasterMACSPortName,
|
||||
int numAxes,
|
||||
int idlePoll, int busyPoll):SINQController
|
||||
(portName, MasterMACSPortName, numAxes)
|
||||
{
|
||||
asynStatus status;
|
||||
static const char *functionName =
|
||||
"MasterMACSController::MasterMACSController";
|
||||
char terminator[2] = "\x03";
|
||||
|
||||
|
||||
/* Connect to MasterMACS controller */
|
||||
status =
|
||||
pasynOctetSyncIO->connect(MasterMACSPortName, 0,
|
||||
&pasynUserController_, NULL);
|
||||
if (status) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: cannot connect to MasterMACS controller\n",
|
||||
functionName);
|
||||
}
|
||||
pasynOctetSyncIO->setOutputEos(pasynUserController_, terminator,
|
||||
strlen(terminator));
|
||||
pasynOctetSyncIO->setInputEos(pasynUserController_, terminator,
|
||||
strlen(terminator));
|
||||
|
||||
|
||||
pAxes_ = (MasterMACSAxis **) (asynMotorController::pAxes_);
|
||||
createParam(EnableAxisString, asynParamInt32, &enableAxis_);
|
||||
createParam(AxisEnabledString, asynParamInt32, &axisEnabled_);
|
||||
callParamCallbacks();
|
||||
|
||||
startPoller(busyPoll/1000., idlePoll/1000., 1);
|
||||
setIdlePollPeriod(idlePoll/1000.);
|
||||
setMovingPollPeriod(busyPoll/1000.);
|
||||
}
|
||||
|
||||
|
||||
/** Creates a new MasterMACSController object.
|
||||
* Configuration command, called directly or from iocsh
|
||||
* \param[in] portName The name of the asyn port that will be created for this driver
|
||||
* \param[in] MasterMACSPortName The name of the drvAsynIPPPort that was created previously to connect to the MasterMACS controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
* \param[in] idlePoll Poll interval when idle in microseconds
|
||||
* \param[in] busyPoll Poll interval when moving in microSeconds
|
||||
*/
|
||||
extern "C" int
|
||||
MasterMACSCreateController(const char *portName,
|
||||
const char *MasterMACSPortName, int numAxes, int idlePoll, int busyPoll)
|
||||
{
|
||||
MasterMACSController *pMasterMACSController
|
||||
= new MasterMACSController(portName, MasterMACSPortName, numAxes, idlePoll, busyPoll);
|
||||
pMasterMACSController = NULL;
|
||||
return (asynSuccess);
|
||||
}
|
||||
|
||||
/** Reports on status of the driver
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* If details > 0 then information is printed about each axis.
|
||||
* After printing controller-specific information it calls asynMotorController::report()
|
||||
*/
|
||||
void
|
||||
MasterMACSController::report(FILE * fp, int level)
|
||||
{
|
||||
fprintf(fp, "MasterMACS motor driver %s, numAxes=%d\n",
|
||||
this->portName, numAxes_);
|
||||
|
||||
// Call the base class method
|
||||
asynMotorController::report(fp, level);
|
||||
}
|
||||
|
||||
/** Returns a pointer to an MasterMACSAxis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
|
||||
MasterMACSAxis *MasterMACSController::getAxis(asynUser * pasynUser)
|
||||
{
|
||||
return static_cast <
|
||||
MasterMACSAxis * >(asynMotorController::getAxis(pasynUser));
|
||||
}
|
||||
|
||||
/** Returns a pointer to an MasterMACSAxis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] axisNo Axis index number. */
|
||||
MasterMACSAxis *MasterMACSController::getAxis(int axisNo)
|
||||
{
|
||||
return static_cast <
|
||||
MasterMACSAxis * >(asynMotorController::getAxis(axisNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* send a command to the MasterMACS and read the reply. Do some error and controller
|
||||
* issue fixing on the way
|
||||
* \param[in] command The command to send
|
||||
* \param[out] reply The controllers reply
|
||||
*/
|
||||
|
||||
asynStatus
|
||||
MasterMACSController::transactController(int axisNo,
|
||||
char command[COMLEN],
|
||||
char reply[COMLEN])
|
||||
{
|
||||
asynStatus status;
|
||||
size_t in, out;
|
||||
int reason, len, idx, lenPayload;
|
||||
unsigned int i;
|
||||
double startTime, now;
|
||||
char *mmacsData =
|
||||
NULL, ackchar, mmacsResponse[COMLEN], hexResponse[256];
|
||||
SINQAxis *axis = getAxis(axisNo);
|
||||
|
||||
pasynOctetSyncIO->flush(pasynUserController_);
|
||||
|
||||
/* read with a short timeout in order to remove duplicate messages
|
||||
* from the line. This also serves to slow down communication
|
||||
*/
|
||||
pasynOctetSyncIO->read(pasynUserController_, mmacsResponse, 35, interMessageSleep/1000., &in, &reason);
|
||||
|
||||
/* pack data for MasterMACS */
|
||||
len = strlen(command) + 6;
|
||||
mmacsData = (char *) malloc(len * sizeof(char));
|
||||
if (!mmacsData) {
|
||||
errlogSevPrintf(errlogMajor,
|
||||
"Failed to allocate memory in MasterMACSController::transactController");
|
||||
return asynError;
|
||||
}
|
||||
mmacsData[0] = 0x05;
|
||||
mmacsData[1] = (char) (len - 2);
|
||||
mmacsData[2] = 0;
|
||||
mmacsData[3] = 0x19;
|
||||
memcpy(mmacsData + 4, command, strlen(command) * sizeof(char));
|
||||
mmacsData[len - 2] = 0x0D;
|
||||
/* 0x03 is appended by asyn */
|
||||
|
||||
/* send the stuff away ... */
|
||||
if(debug) {
|
||||
errlogSevPrintf(errlogMajor,"Sending command: %s\n", command);
|
||||
}
|
||||
|
||||
startTime = DoubleTime();
|
||||
status =
|
||||
pasynOctetSyncIO->writeRead(pasynUserController_, mmacsData,
|
||||
len - 1, mmacsResponse, 35, 20., &out,
|
||||
&in, &reason);
|
||||
|
||||
if(timeDebug) {
|
||||
now = DoubleTime();
|
||||
if((now - startTime) > 1.) {
|
||||
errlogSevPrintf(errlogMajor, "Unusual long response time %lf to command %s\n", (now - startTime), command);
|
||||
}
|
||||
}
|
||||
|
||||
if (status != asynSuccess) {
|
||||
if (axis != NULL) {
|
||||
errlogSevPrintf(errlogMajor,
|
||||
"Lost connection to motor controller,a axis %d, reason %d",
|
||||
axisNo, reason);
|
||||
axis->updateMsgTxtFromDriver
|
||||
("Lost connection to motor controller");
|
||||
return status;
|
||||
}
|
||||
errlogSevPrintf(errlogMajor,
|
||||
"Lost connection to motor controller without axis, reason %d",
|
||||
reason);
|
||||
return status;
|
||||
}
|
||||
free(mmacsData);
|
||||
|
||||
/* format and print the response in hex for debugging purposes */
|
||||
if(debug) {
|
||||
for(i = 0, idx = 0; i < in; i++){
|
||||
sprintf(hexResponse + idx, "%02x ", (unsigned int)mmacsResponse[i]);
|
||||
idx = strlen(hexResponse);
|
||||
}
|
||||
errlogSevPrintf(errlogMajor,"Reply in hex: %s\n", hexResponse);
|
||||
}
|
||||
|
||||
/* Here we have read the data from the MasterMACS. We proceed to extract
|
||||
* the payload and the state of the ACK byte and place that reply into data
|
||||
*/
|
||||
if ((in < 33)) {
|
||||
errlogSevPrintf(errlogMajor,
|
||||
"MasterMACS only sent %d bytes, 34 expected",
|
||||
(int) in);
|
||||
return asynError;
|
||||
}
|
||||
/*
|
||||
* Unpack the MasterMACS message, start by finding <CR>
|
||||
*/
|
||||
for (i = 4, idx = 0; i < 34; i++) {
|
||||
if (mmacsResponse[i] == 0x0d) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//errlogSevPrintf(errlogMajor, "Found <CR> at %d", idx);
|
||||
|
||||
/* one character before <CR> is the ACK, if it is there */
|
||||
ackchar = mmacsResponse[idx - 1];
|
||||
if (ackchar == 0x06) {
|
||||
strcpy(reply, "ACK:");
|
||||
} else if (ackchar == 0x15) {
|
||||
strcpy(reply, "NAK:");
|
||||
errlogSevPrintf(errlogMajor,
|
||||
"MasterMACS responded with NAK to %s\n", command);
|
||||
status = asynError;
|
||||
} else {
|
||||
/* the MasterMACS does not always send a ACK/NAK on read */
|
||||
strcpy(reply, "NON:");
|
||||
}
|
||||
//errlogSevPrintf(errlogMajor, "Reply after testing ACK byte: %s ", reply);
|
||||
|
||||
/* copy data */
|
||||
lenPayload = idx - 4;
|
||||
memcpy(reply + 4, mmacsResponse + 4, lenPayload);
|
||||
reply[4 + lenPayload] = (char) 0;
|
||||
if(debug) {
|
||||
errlogSevPrintf(errlogMajor, "Completed reply at end of transactController: %s ", reply);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------------------------*/
|
||||
asynStatus
|
||||
MasterMACSController::readInt32(asynUser * pasynUser,
|
||||
epicsInt32 * value)
|
||||
{
|
||||
|
||||
int function = pasynUser->reason;
|
||||
MasterMACSAxis *pAxis = NULL;
|
||||
int devStatus, isOn;
|
||||
|
||||
pAxis = (MasterMACSAxis *) (this->getAxis(pasynUser));
|
||||
if (!pAxis) {
|
||||
return asynError;
|
||||
}
|
||||
if (function == axisEnabled_) {
|
||||
devStatus = pAxis->readStatus();
|
||||
isOn = pAxis->isOn(devStatus);
|
||||
// errlogPrintf("isOn in readInt32: %d, devStatus = %d\n", isOn, devStatus);
|
||||
pAxis->setIntegerParam(enableAxis_, isOn);
|
||||
pAxis->setIntegerParam(axisEnabled_, isOn);
|
||||
*value = isOn;
|
||||
callParamCallbacks();
|
||||
return asynSuccess;
|
||||
}
|
||||
return asynMotorController::readInt32(pasynUser, value);
|
||||
}
|
||||
/*------------------------------------------------------------------------------------------*/
|
||||
|
||||
asynStatus
|
||||
MasterMACSController::writeInt32(asynUser * pasynUser,
|
||||
epicsInt32 value)
|
||||
{
|
||||
int function = pasynUser->reason;
|
||||
asynStatus status = asynSuccess;
|
||||
MasterMACSAxis *pAxis = NULL;
|
||||
char command[64] = { 0 };
|
||||
char response[64] = { 0 };
|
||||
int devStatus;
|
||||
bool moving;
|
||||
time_t startTime;
|
||||
|
||||
pAxis = (MasterMACSAxis *) this->getAxis(pasynUser);
|
||||
if (!pAxis) {
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/* Set the parameter and readback in the parameter library. This may be
|
||||
* overwritten when we read back the status at the end, but that's OK */
|
||||
pAxis->setIntegerParam(function, value);
|
||||
|
||||
if(function == motorStop_){
|
||||
errlogPrintf("Stop called with value %d\n", value);
|
||||
double accel;
|
||||
getDoubleParam(pAxis->axisNo_, motorAccel_, &accel);
|
||||
status = pAxis->stop(accel);
|
||||
return status;
|
||||
}
|
||||
if (function == enableAxis_) {
|
||||
/*
|
||||
* Read the status in order to prevent execssive commands
|
||||
*/
|
||||
devStatus = pAxis->readStatus();
|
||||
if (value == 1 && !pAxis->isOn(devStatus) ) {
|
||||
/* download parameters, does not work as of now */
|
||||
/*
|
||||
sprintf(command, "%dS85=1.", pAxis->axisNo_);
|
||||
status = transactController(pAxis->axisNo_, command, response);
|
||||
*/
|
||||
|
||||
/* actually enable */
|
||||
sprintf(command, "%dS04=1", pAxis->axisNo_);
|
||||
status = transactController(pAxis->axisNo_, command, response);
|
||||
} else if(pAxis->isOn(devStatus)) {
|
||||
// only send command when necessary
|
||||
sprintf(command, "%dS04=0", pAxis->axisNo_);
|
||||
status = transactController(pAxis->axisNo_, command, response);
|
||||
} else {
|
||||
// nothing to do
|
||||
return status;
|
||||
}
|
||||
if (status == asynSuccess) {
|
||||
pAxis->updateMsgTxtFromDriver("");
|
||||
} else {
|
||||
errlogPrintf("MMACS: Failure to enable or disable axis %d",
|
||||
pAxis->axisNo_);
|
||||
}
|
||||
/*
|
||||
* wait max 2 seconds for success of the operation
|
||||
*/
|
||||
startTime = time(NULL);
|
||||
while(time(NULL) < startTime + 2.){
|
||||
// wait for the change to happen
|
||||
devStatus = pAxis->readStatus();
|
||||
// errlogPrintf("MMACS: switching enable: target, devStatus, isOn: %d, %d, %d\n", value, devStatus, pAxis->isOn(devStatus));
|
||||
if(pAxis->isOn(devStatus) == value){
|
||||
pAxis->active = true;
|
||||
pAxis->poll(&moving); // to update the Enable_RBV field
|
||||
pAxis->active = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
usleep(200);
|
||||
}
|
||||
errlogPrintf("MMACS: Failed to dis/enable motor %d within 2 seconds\n", pAxis->axisNo_);
|
||||
}
|
||||
return asynMotorController::writeInt32(pasynUser, value);
|
||||
}
|
||||
|
||||
|
||||
// These are the MasterMACSAxis methods
|
||||
|
||||
/** Creates a new MasterMACSAxis object.
|
||||
* \param[in] pC Pointer to the MasterMACSController to which this axis belongs.
|
||||
* \param[in] axisNo Index number of this axis, range 0 to pC->numAxes_-1.
|
||||
*
|
||||
* Initializes register numbers, etc.
|
||||
*/
|
||||
MasterMACSAxis::MasterMACSAxis(MasterMACSController * pC, int axisNo):SINQAxis(pC, axisNo),
|
||||
pC_(pC)
|
||||
{
|
||||
hasStarted = false;
|
||||
active = false;
|
||||
errorCodeFound = 0;
|
||||
/*
|
||||
* Try to read the initial enable status of the motor
|
||||
*/
|
||||
int devStatus = readStatus();
|
||||
setIntegerParam(pC_->enableAxis_, isOn(devStatus));
|
||||
setIntegerParam(pC_->axisEnabled_, isOn(devStatus));
|
||||
lastPoll = -1;
|
||||
}
|
||||
|
||||
/** Reports on status of the axis
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
*
|
||||
* After printing device-specific information calls asynMotorAxis::report()
|
||||
*/
|
||||
void MasterMACSAxis::report(FILE * fp, int level)
|
||||
{
|
||||
if (level > 0) {
|
||||
fprintf(fp, " axis %d\n", axisNo_);
|
||||
}
|
||||
}
|
||||
|
||||
int MasterMACSAxis::readStatus()
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN], *pPtr;
|
||||
float fval;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* The MasterMACS sends invalid responses with a low frequency.
|
||||
* Therefore I send cached status responses in such a case in order
|
||||
* to help the logic everywhere else in the code.
|
||||
*/
|
||||
sprintf(command, "%dR10", axisNo_);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
if (status == asynError) {
|
||||
return oldStatus;
|
||||
}
|
||||
pPtr = strstr(reply, "=");
|
||||
if(pPtr) {
|
||||
sscanf(pPtr + 1, "%f", &fval);
|
||||
oldStatus = (int) fval;
|
||||
return oldStatus;
|
||||
} else {
|
||||
errlogPrintf("MMACS: invalid status reponse %s on axis %d\n", reply, axisNo_);
|
||||
return oldStatus;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int MasterMACSAxis::isOn(int axisStatus)
|
||||
{
|
||||
if (CHECK_BIT(axisStatus, 0) && CHECK_BIT(axisStatus, 1)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
asynStatus
|
||||
MasterMACSAxis::move(double position, int relative, double minVelocity,
|
||||
double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status = asynSuccess;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
int devStatus;
|
||||
|
||||
//errlogPrintf("minVelocity = %f, maxVelocity = %f\n", minVelocity, maxVelocity);
|
||||
memset(command, 0, COMLEN * sizeof(char));
|
||||
|
||||
/* clear motor error message */
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
/*
|
||||
* reset error code
|
||||
*/
|
||||
if(errorCodeFound){
|
||||
sprintf(command, "%dS17=0", axisNo_);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
errorCodeFound = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* only start if the thing is On
|
||||
*/
|
||||
devStatus = readStatus();
|
||||
|
||||
/*
|
||||
* set speed
|
||||
*/
|
||||
/* temporarily disabled in order to get the basic logic right
|
||||
sprintf(command, "%dS05=%f", axisNo_, maxVelocity / 1000);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
*/
|
||||
|
||||
if (relative) {
|
||||
position += this->position;
|
||||
}
|
||||
|
||||
if(isOn(devStatus) && active == false) {
|
||||
errlogPrintf("Starting axis %d with destination %f\n", axisNo_,
|
||||
position / 1000.);
|
||||
|
||||
/* send target position */
|
||||
sprintf(command, "%dS02= %.3f", axisNo_, position / 1000.);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
if (status == asynError) {
|
||||
errlogPrintf("MMACS: failed to set target on %d\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Failed to send target position");
|
||||
return status;
|
||||
}
|
||||
|
||||
/* send move command */
|
||||
sprintf(command, "%dS00=1", axisNo_);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
if (status == asynError) {
|
||||
errlogPrintf("MMACS: failed to start axis %d\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Failed to start axis");
|
||||
return status;
|
||||
}
|
||||
hasStarted = true;
|
||||
homing = 0;
|
||||
active = true;
|
||||
usleep(500);
|
||||
lastPositionUpdate = time(NULL);
|
||||
} else {
|
||||
errlogPrintf("MMACS: axis %d disabled, cannot start\n", axisNo_);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus
|
||||
MasterMACSAxis::home(double minVelocity, double maxVelocity,
|
||||
double acceleration, int forwards)
|
||||
{
|
||||
asynStatus status;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
int devStatus;
|
||||
|
||||
memset(command, 0, COMLEN * sizeof(char));
|
||||
|
||||
/*
|
||||
* test if the thing is On
|
||||
*/
|
||||
devStatus = readStatus();
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
if(isOn(devStatus)){
|
||||
sprintf(command, "%dS00=9", axisNo_);
|
||||
homing = 1;
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
hasStarted = true;
|
||||
active = true;
|
||||
lastPositionUpdate = time(NULL);
|
||||
return status;
|
||||
} else {
|
||||
errlogPrintf("MMACS: cannot home disabled axis %d\n", axisNo_);
|
||||
return asynError;
|
||||
}
|
||||
}
|
||||
|
||||
asynStatus
|
||||
MasterMACSAxis::moveVelocity(double minVelocity, double maxVelocity,
|
||||
double acceleration)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor,
|
||||
"This controller does not support the jog feature");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynStatus MasterMACSAxis::stop(double acceleration)
|
||||
{
|
||||
asynStatus status = asynSuccess;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
int devStatus;
|
||||
|
||||
memset(command, 0, COMLEN * sizeof(char));
|
||||
|
||||
devStatus = readStatus();
|
||||
if(!CHECK_BIT(devStatus, 10)) {
|
||||
// only try to stop when running ...
|
||||
sprintf(command, "%dS00=8", axisNo_);
|
||||
status = pC_->transactController(axisNo_, command, reply);
|
||||
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Axis interrupted");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus MasterMACSAxis::setPosition(double position)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor,
|
||||
"This controller does not support setting position");
|
||||
updateMsgTxtFromDriver("Controller does not support setPosition");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynStatus MasterMACSAxis::setClosedLoop(bool closedLoop)
|
||||
{
|
||||
/*
|
||||
This belongs into the Kingdom of Electronics.
|
||||
We do not do this.
|
||||
*/
|
||||
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/** Polls the axis.
|
||||
* This function reads the motor position, the limit status, the home status, the moving status,
|
||||
* and the drive power-on status.
|
||||
* It calls setIntegerParam() and setDoubleParam() for each item that it polls,
|
||||
* and then calls callParamCallbacks() at the end.
|
||||
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
|
||||
asynStatus MasterMACSAxis::poll(bool * moving)
|
||||
{
|
||||
asynStatus comStatus = asynSuccess;
|
||||
char command[COMLEN], reply[COMLEN], *pPtr, buffer[80];
|
||||
unsigned int errCode, derCode, devStatus = 0;
|
||||
float errStatus;
|
||||
struct tm* tm_info;
|
||||
time_t timer;
|
||||
|
||||
/*
|
||||
* EPICS always polls all motors on a controller when one motor is active.
|
||||
* In order to reduce load on the controller we check if we are active and
|
||||
* if not return old stuff
|
||||
*/
|
||||
if(active == false && time(NULL) < lastPoll + pC_->idlePollPeriod_) {
|
||||
*moving = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
timer = time(NULL);
|
||||
tm_info = localtime(&timer);
|
||||
|
||||
strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
errlogPrintf("poll called at %s on axis %d \n",
|
||||
buffer, axisNo_ );
|
||||
lastPoll = time(NULL);
|
||||
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
memset(command, 0, COMLEN * sizeof(char));
|
||||
|
||||
// Read the current motor position
|
||||
sprintf(command, "%dR12", axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_, command, reply);
|
||||
if(comStatus != asynError) {
|
||||
pPtr = strstr(reply, "=");
|
||||
if(pPtr) {
|
||||
sscanf(pPtr + 1, "%lf", &position);
|
||||
setDoubleParam(pC_->motorPosition_, position * 1000.);
|
||||
setDoubleParam(pC_->motorEncoderPosition_, position * 1000.);
|
||||
/*
|
||||
* keep track of position in order to check for a stuck motor later
|
||||
*/
|
||||
if(ABS(position - oldPosition) > 1){
|
||||
oldPosition = position;
|
||||
lastPositionUpdate = time(NULL);
|
||||
}
|
||||
} else {
|
||||
errlogPrintf("MMACS: Invalid response asking position on %d\n", axisNo_);
|
||||
}
|
||||
} else {
|
||||
errlogPrintf("MMACS: communication problem reading axis %d position\n", axisNo_);
|
||||
*moving = false;
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
// Read the overall status of this motor */
|
||||
devStatus = readStatus();
|
||||
if(debug || timeDebug) {
|
||||
errlogPrintf("Axis %d, position %lf, devStatus %d\n", axisNo_,
|
||||
position, devStatus);
|
||||
}
|
||||
|
||||
// Check for the thing being in a bad state
|
||||
if(CHECK_BIT(devStatus, 6)) {
|
||||
*moving = false;
|
||||
active = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
updateMsgTxtFromDriver("AXIS dead");
|
||||
goto skip;
|
||||
}
|
||||
|
||||
|
||||
setIntegerParam(pC_->enableAxis_, isOn(devStatus));
|
||||
setIntegerParam(pC_->axisEnabled_, isOn(devStatus));
|
||||
|
||||
// Check if the motor is disabled
|
||||
if (!isOn(devStatus)) {
|
||||
updateMsgTxtFromDriver("Axis disabled");
|
||||
*moving = false;
|
||||
active = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the motor has never run, the status bit 10 is invalid
|
||||
*/
|
||||
if (!hasStarted) {
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
active = false;
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/*
|
||||
* We may have a valid status bit...
|
||||
*/
|
||||
if (!CHECK_BIT(devStatus, 10)) {
|
||||
/* we are still creeping along .... */
|
||||
*moving = true;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
if(time(NULL) > lastPositionUpdate + 120) {
|
||||
// we are detecting a stuck motor
|
||||
errlogPrintf("MMACS: axis %d is STUCK!!\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Axis STUCK!!");
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
*moving = false;
|
||||
active = false;
|
||||
}
|
||||
goto skip;
|
||||
}
|
||||
|
||||
|
||||
/*we are done moving */
|
||||
*moving = false;
|
||||
active = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
/* when homing, set the proper flag */
|
||||
if (homing) {
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
}
|
||||
|
||||
/* check for limit switches*/
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, false);
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, false);
|
||||
if (CHECK_BIT(devStatus, 11)) {
|
||||
errlogSevPrintf(errlogMajor, "Limit bit in status code %d", axisNo_);
|
||||
/* guessing which limit has been hit ... */
|
||||
if (position > 0) {
|
||||
updateMsgTxtFromDriver("Hit positive limit switch");
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
} else {
|
||||
updateMsgTxtFromDriver("Hit negative limit switch");
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
}
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/* check for error conditions */
|
||||
if (CHECK_BIT(devStatus, 3)) {
|
||||
/* read error codes */
|
||||
sprintf(command, "%dR11", axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_, command, reply);
|
||||
if (comStatus != asynError) {
|
||||
pPtr = strstr(reply, "=");
|
||||
if(pPtr) {
|
||||
sscanf(pPtr + 1, "%f", &errStatus);
|
||||
errCode = (unsigned int) errStatus;
|
||||
|
||||
} else {
|
||||
errlogPrintf("MMACS: axis %d received invalid reply asking for error code \n", axisNo_);
|
||||
errCode = 0;
|
||||
goto skip;
|
||||
}
|
||||
} else {
|
||||
errlogPrintf("MMACS: axis %d failed reading error code \n", axisNo_);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
sprintf(command, "%dR18", axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_, command, reply);
|
||||
if (comStatus != asynError) {
|
||||
pPtr = strstr(reply, "=");
|
||||
if(pPtr) {
|
||||
sscanf(pPtr + 1, "%f", &errStatus);
|
||||
derCode = (unsigned int) errStatus;
|
||||
} else {
|
||||
errlogPrintf("MMACS: malformed reply for R18: %s, on axis %d\n", reply, axisNo_);
|
||||
derCode = 0;
|
||||
goto skip;
|
||||
}
|
||||
} else {
|
||||
errlogPrintf("MMACS: axis %d failed reading extended error code R18 \n", axisNo_);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if(debug) {
|
||||
errlogPrintf("Axis %d, errCode(R11) %d, derCode(R18) %d\n", axisNo_,
|
||||
errCode, derCode);
|
||||
}
|
||||
|
||||
if (errCode == 0) {
|
||||
errlogSevPrintf(errlogMajor,
|
||||
"Fault bit in status code, but no error code on %d\n", axisNo_);
|
||||
updateMsgTxtFromDriver ("Fault bit in status code without error code");
|
||||
goto skip;
|
||||
}
|
||||
errorCodeFound = 1;
|
||||
|
||||
if (CHECK_BIT(errCode, 0)) {
|
||||
errlogSevPrintf(errlogMajor, "CAN error on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("CAN error");
|
||||
} else if (CHECK_BIT(errCode, 1)) {
|
||||
errlogSevPrintf(errlogMajor, "Short circuit on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Short circuit");
|
||||
} else if (CHECK_BIT(errCode, 2)) {
|
||||
errlogSevPrintf(errlogMajor, "Invalide Setup on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Invalid Setup");
|
||||
} else if (CHECK_BIT(errCode, 3)) {
|
||||
errlogSevPrintf(errlogMajor, "Control error on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Control error");
|
||||
} else if (CHECK_BIT(errCode, 4)) {
|
||||
errlogSevPrintf(errlogMajor, "CAN communication error on %d",
|
||||
axisNo_);
|
||||
updateMsgTxtFromDriver("CAN communication error");
|
||||
} else if (CHECK_BIT(errCode, 5)) {
|
||||
errlogSevPrintf(errlogMajor, "Feedback error on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Feedback error");
|
||||
} else if (CHECK_BIT(errCode, 6)) {
|
||||
updateMsgTxtFromDriver("Hit positive limit switch");
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
} else if (CHECK_BIT(errCode, 7)) {
|
||||
updateMsgTxtFromDriver("Hit negative limit switch");
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
} else if (CHECK_BIT(errCode, 8)) {
|
||||
errlogSevPrintf(errlogMajor, "Over current %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Over current");
|
||||
} else if (CHECK_BIT(errCode, 9)) {
|
||||
errlogSevPrintf(errlogMajor, "I2T protection on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("I2t protection");
|
||||
} else if (CHECK_BIT(errCode, 10)) {
|
||||
errlogSevPrintf(errlogMajor, "Over heated motor on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Motor overheated");
|
||||
} else if (CHECK_BIT(errCode, 11)) {
|
||||
errlogSevPrintf(errlogMajor, "Over temperature drive on %d",
|
||||
axisNo_);
|
||||
updateMsgTxtFromDriver("Over temperature drive");
|
||||
} else if (CHECK_BIT(errCode, 12)) {
|
||||
errlogSevPrintf(errlogMajor, "Over voltage on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Over voltage");
|
||||
} else if (CHECK_BIT(errCode, 13)) {
|
||||
errlogSevPrintf(errlogMajor, "Under voltage on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Under voltage");
|
||||
} else if (CHECK_BIT(errCode, 14)) {
|
||||
errlogSevPrintf(errlogMajor, "Command error on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Command error");
|
||||
} else if (CHECK_BIT(errCode, 15)) {
|
||||
errlogSevPrintf(errlogMajor, "Motor disabled on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Motor disabled");
|
||||
}
|
||||
}
|
||||
|
||||
skip:
|
||||
callParamCallbacks();
|
||||
return comStatus;
|
||||
}
|
||||
|
||||
/** Code for iocsh registration */
|
||||
static const iocshArg
|
||||
MasterMACSCreateControllerArg0 = { "Port name", iocshArgString };
|
||||
|
||||
static const iocshArg
|
||||
MasterMACSCreateControllerArg1 =
|
||||
{ "MasterMACS port name", iocshArgString };
|
||||
|
||||
static const iocshArg
|
||||
MasterMACSCreateControllerArg2 = { "Number of axes", iocshArgInt };
|
||||
|
||||
static const iocshArg
|
||||
MasterMACSCreateControllerArg3 = { "idlePoll", iocshArgInt };
|
||||
|
||||
static const iocshArg
|
||||
MasterMACSCreateControllerArg4 = { "busyPoll", iocshArgInt };
|
||||
|
||||
|
||||
static const iocshArg *const
|
||||
MasterMACSCreateControllerArgs[] = { &MasterMACSCreateControllerArg0,
|
||||
&MasterMACSCreateControllerArg1,
|
||||
&MasterMACSCreateControllerArg2,
|
||||
&MasterMACSCreateControllerArg3,
|
||||
&MasterMACSCreateControllerArg4
|
||||
};
|
||||
|
||||
static const iocshFuncDef
|
||||
MasterMACSCreateControllerDef =
|
||||
{ "MasterMACSCreateController", 5, MasterMACSCreateControllerArgs };
|
||||
static void MasterMACSCreateContollerCallFunc(const iocshArgBuf * args)
|
||||
{
|
||||
MasterMACSCreateController(args[0].sval, args[1].sval, args[2].ival,
|
||||
args[3].ival, args[4].ival);
|
||||
}
|
||||
|
||||
/**
|
||||
* C wrapper for the MasterMACSAxis constructor.
|
||||
* See MasterMAXSAxis::MasterMACSAxis.
|
||||
*
|
||||
*/
|
||||
asynStatus MasterMACSCreateAxis(const char *MasterMACSPort, /* specify which controller by port name */
|
||||
int axis)
|
||||
{ /* axis number (start from 1). */
|
||||
MasterMACSController *pC;
|
||||
MasterMACSAxis *pAxis;
|
||||
|
||||
static const char *functionName = "MasterMACSCreateAxis";
|
||||
|
||||
pC = (MasterMACSController *) findAsynPortDriver(MasterMACSPort);
|
||||
if (!pC) {
|
||||
printf("%s:%s: Error port %s not found\n", "MasterMACSDriver",
|
||||
functionName, MasterMACSPort);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
pC->lock();
|
||||
pAxis = new MasterMACSAxis(pC, axis);
|
||||
pAxis = NULL;
|
||||
pC->unlock();
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/* MasterMACSCreateAxis */
|
||||
static const iocshArg
|
||||
MasterMACSCreateAxisArg0 = { "Controller port name", iocshArgString };
|
||||
|
||||
static const iocshArg
|
||||
MasterMACSCreateAxisArg1 = { "Axis number", iocshArgInt };
|
||||
|
||||
static const iocshArg *const
|
||||
MasterMACSCreateAxisArgs[] = { &MasterMACSCreateAxisArg0,
|
||||
&MasterMACSCreateAxisArg1
|
||||
};
|
||||
|
||||
static const iocshFuncDef
|
||||
configMasterMACSAxis =
|
||||
{ "MasterMACSCreateAxis", 2, MasterMACSCreateAxisArgs };
|
||||
|
||||
static void configMasterMACSAxisCallFunc(const iocshArgBuf * args)
|
||||
{
|
||||
MasterMACSCreateAxis(args[0].sval, args[1].ival);
|
||||
}
|
||||
|
||||
static void MasterMACSRegister(void)
|
||||
{
|
||||
iocshRegister(&MasterMACSCreateControllerDef,
|
||||
MasterMACSCreateContollerCallFunc);
|
||||
iocshRegister(&configMasterMACSAxis, configMasterMACSAxisCallFunc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(MasterMACSRegister);
|
||||
}
|
77
sinqEPICSApp/src/MasterMACSDriver.h
Normal file
77
sinqEPICSApp/src/MasterMACSDriver.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Driver for the MasterMACS motor controllers used at SINQ.
|
||||
For documentation see the standard manuals area for SINQ or
|
||||
Marcel Schildt.
|
||||
|
||||
The MasterMACS has a special line protocol which is implemented in
|
||||
drvAsynMMACSPort.c. The driver will not work with a standard asyn IPPort,
|
||||
only with the special one.
|
||||
|
||||
Mark Koennecke, March, August, 2023
|
||||
*/
|
||||
|
||||
#include "SINQController.h"
|
||||
#include "SINQAxis.h"
|
||||
|
||||
#define COMLEN 50
|
||||
|
||||
class MasterMACSAxis : public SINQAxis
|
||||
{
|
||||
public:
|
||||
/* These are the methods we override from the base class */
|
||||
MasterMACSAxis(class MasterMACSController *pC, int axis);
|
||||
void report(FILE *fp, int level);
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
|
||||
asynStatus stop(double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus setPosition(double position);
|
||||
asynStatus setClosedLoop(bool closedLoop);
|
||||
|
||||
private:
|
||||
MasterMACSController *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
|
||||
* Abbreviated because it is used very frequently */
|
||||
double position;
|
||||
double oldPosition;
|
||||
time_t lastPositionUpdate;
|
||||
int homing;
|
||||
int hasStarted; /* The motor status is invalid if the thing has not run once */
|
||||
int isOn(int axisStatus);
|
||||
int readStatus();
|
||||
int errorCodeFound;
|
||||
int oldStatus;
|
||||
|
||||
bool active;
|
||||
time_t lastPoll;
|
||||
|
||||
friend class MasterMACSController;
|
||||
};
|
||||
|
||||
|
||||
#define EnableAxisString "ENABLE_AXIS"
|
||||
#define AxisEnabledString "AXIS_ENABLED"
|
||||
|
||||
class MasterMACSController : public SINQController {
|
||||
public:
|
||||
MasterMACSController(const char *portName, const char *MasterMACSPortName,
|
||||
int numAxes, int idlePoll, int busyPoll);
|
||||
|
||||
void report(FILE *fp, int level);
|
||||
MasterMACSAxis* getAxis(asynUser *pasynUser);
|
||||
MasterMACSAxis* getAxis(int axisNo);
|
||||
|
||||
// overloaded because we want to enable/disable the motor
|
||||
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
|
||||
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
|
||||
|
||||
friend class MasterMACSAxis;
|
||||
private:
|
||||
asynUser *pasynUserController_;
|
||||
MasterMACSAxis **pAxes_; /**< Array of pointers to axis objects */
|
||||
|
||||
|
||||
asynStatus transactController(int axis, char command[COMLEN], char reply[COMLEN]);
|
||||
int enableAxis_;
|
||||
int axisEnabled_;
|
||||
};
|
@ -14,6 +14,9 @@ list to the motor controller constructor.
|
||||
Mark Koennecke
|
||||
July 2015
|
||||
|
||||
Modified to use the MsgTxt field for SINQ
|
||||
|
||||
Mark Koennecke, January 2019
|
||||
*/
|
||||
|
||||
|
||||
@ -43,12 +46,7 @@ July 2015
|
||||
* \param[in] NanotecPortName The name of the drvAsynSerialPort that was created previously to connect to the Nanotec controller
|
||||
*/
|
||||
NanotecController::NanotecController(const char *portName, const char *NanotecPortName, int motCount, const char *bus)
|
||||
: asynMotorController(portName, motCount+1, 0,
|
||||
0, // No additional interfaces beyond those in base class
|
||||
0, // No additional callback interfaces beyond those in base class
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
1, // autoconnect
|
||||
0, 0) // Default priority and stack size
|
||||
: SINQController(portName, NanotecPortName, motCount+1)
|
||||
{
|
||||
int axis, busAddress;
|
||||
asynStatus status;
|
||||
@ -136,11 +134,11 @@ NanotecAxis* NanotecController::getAxis(int axisNo)
|
||||
* Initializes register numbers, etc.
|
||||
*/
|
||||
NanotecAxis::NanotecAxis(NanotecController *pC, int axisNo, int busAddress)
|
||||
: asynMotorAxis(pC, axisNo),
|
||||
: SINQAxis(pC, axisNo),
|
||||
pC_(pC)
|
||||
{
|
||||
this->busAddress = busAddress;
|
||||
|
||||
next_poll = -1;
|
||||
}
|
||||
|
||||
|
||||
@ -162,17 +160,21 @@ void NanotecAxis::report(FILE *fp, int level)
|
||||
//asynMotorAxis::report(fp, level);
|
||||
}
|
||||
|
||||
asynStatus NanotecController::transactController(char command[COMLEN], char reply[COMLEN])
|
||||
asynStatus NanotecController::transactController(int axisNo, char command[COMLEN], char reply[COMLEN])
|
||||
{
|
||||
asynStatus status;
|
||||
size_t in, out;
|
||||
int reason;
|
||||
SINQAxis *axis = getAxis(axisNo);
|
||||
|
||||
pasynOctetSyncIO->flush(pasynUserController_);
|
||||
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
if(status != asynSuccess){
|
||||
if(axis != NULL){
|
||||
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -200,6 +202,8 @@ asynStatus NanotecAxis::move(double position, int relative, double minVelocity,
|
||||
size_t in, out;
|
||||
int reason;
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
// status = sendAccelAndVelocity(acceleration, maxVelocity);
|
||||
|
||||
if (relative) {
|
||||
@ -215,7 +219,7 @@ asynStatus NanotecAxis::move(double position, int relative, double minVelocity,
|
||||
set mode
|
||||
*/
|
||||
snprintf(command,sizeof(command),"#%dp2",busAddress);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(status != asynSuccess){
|
||||
return status;
|
||||
}
|
||||
@ -224,7 +228,7 @@ asynStatus NanotecAxis::move(double position, int relative, double minVelocity,
|
||||
set target
|
||||
*/
|
||||
snprintf(command,sizeof(command),"#%ds%d",busAddress, (int)position);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(status != asynSuccess){
|
||||
return status;
|
||||
}
|
||||
@ -233,7 +237,7 @@ asynStatus NanotecAxis::move(double position, int relative, double minVelocity,
|
||||
and start..
|
||||
*/
|
||||
snprintf(command,sizeof(command),"#%dA",busAddress);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(status != asynSuccess){
|
||||
return status;
|
||||
}
|
||||
@ -250,11 +254,13 @@ asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acce
|
||||
|
||||
setIntegerParam(pC_->motorStatusAtHome_, false);
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
/*
|
||||
reset positioning errors
|
||||
*/
|
||||
snprintf(command,sizeof(command),"#%dD",busAddress);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(status != asynSuccess){
|
||||
return status;
|
||||
}
|
||||
@ -263,7 +269,7 @@ asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acce
|
||||
set mode
|
||||
*/
|
||||
snprintf(command,sizeof(command),"#%dp4",busAddress);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(status != asynSuccess){
|
||||
return status;
|
||||
}
|
||||
@ -273,7 +279,7 @@ asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acce
|
||||
set direction
|
||||
*/
|
||||
snprintf(command,sizeof(command),"#%dd0",busAddress);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(status != asynSuccess){
|
||||
return status;
|
||||
}
|
||||
@ -283,7 +289,7 @@ asynStatus NanotecAxis::home(double minVelocity, double maxVelocity, double acce
|
||||
and start..
|
||||
*/
|
||||
snprintf(command,sizeof(command),"#%dA",busAddress);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(status != asynSuccess){
|
||||
return status;
|
||||
}
|
||||
@ -304,6 +310,7 @@ asynStatus NanotecAxis::moveVelocity(double minVelocity, double maxVelocity, dou
|
||||
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
|
||||
// functionName, minVelocity, maxVelocity, acceleration);
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
|
||||
if (maxVelocity > 0.) {
|
||||
@ -326,7 +333,7 @@ asynStatus NanotecAxis::stop(double acceleration )
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
sprintf(command, "#%dS1", busAddress);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
|
||||
|
||||
return status;
|
||||
@ -338,8 +345,10 @@ asynStatus NanotecAxis::setPosition(double position)
|
||||
//static const char *functionName = "NanotecAxis::setPosition";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
sprintf(command, "#%dD%d", busAddress, (int)position);
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
@ -368,7 +377,7 @@ asynStatus NanotecAxis::poll(bool *moving)
|
||||
asynStatus comStatus;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
char *pPtr;
|
||||
int posVal, statVal;
|
||||
int posVal, statVal, count = 0;
|
||||
double lowLim, highLim;
|
||||
|
||||
|
||||
@ -380,28 +389,40 @@ asynStatus NanotecAxis::poll(bool *moving)
|
||||
|
||||
// Read the current motor position
|
||||
sprintf(command,"#%dC", busAddress);
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus) goto skip;
|
||||
|
||||
pPtr = strchr(reply,'C');
|
||||
pPtr++;
|
||||
posVal = atoi(pPtr);
|
||||
if(pPtr){
|
||||
pPtr++;
|
||||
count = sscanf(pPtr,"%d", &posVal);
|
||||
}
|
||||
if(pPtr == NULL || count < 1) {
|
||||
errlogPrintf("Invalid response %s for #C received for axis %d, address %d\n", reply, axisNo_, busAddress);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
errlogPrintf("Axis %d, reply %s, position %d\n", axisNo_, reply, posVal);
|
||||
//errlogPrintf("Axis %d, reply %s, position %d\n", axisNo_, reply, posVal);
|
||||
setDoubleParam(pC_->motorPosition_, (double)posVal);
|
||||
//setDoubleParam(pC_->motorEncoderPosition_, position);
|
||||
|
||||
|
||||
// Read the moving status of this motor
|
||||
sprintf(command,"#%d$",busAddress);
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus) goto skip;
|
||||
|
||||
pPtr = strchr(reply,'$');
|
||||
pPtr++;
|
||||
statVal = atoi(pPtr);
|
||||
errlogPrintf("Axis %d, reply %s, statVal = %d\n",
|
||||
axisNo_, reply, statVal);
|
||||
if(pPtr) {
|
||||
pPtr++;
|
||||
count = sscanf(pPtr, "%d", &statVal);
|
||||
}
|
||||
if(pPtr == NULL || count < 1) {
|
||||
errlogPrintf("Invalid response %s for #$ received for axis %d busAddress %d\n", reply, axisNo_, busAddress);
|
||||
return asynError;
|
||||
}
|
||||
//errlogPrintf("Axis %d, reply %s, statVal = %d\n",
|
||||
// axisNo_, reply, statVal);
|
||||
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
*moving = true;
|
||||
@ -438,10 +459,13 @@ asynStatus NanotecAxis::poll(bool *moving)
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Limit or other positioning problem at %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Positioning problem");
|
||||
if(ABS(posVal - lowLim) < ABS(posVal - highLim)){
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
updateMsgTxtFromDriver("Low Limit Hit");
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
updateMsgTxtFromDriver("High Limit Hit");
|
||||
}
|
||||
*moving = false;
|
||||
}
|
||||
|
@ -5,15 +5,18 @@ USAGE... Motor driver support for the Nanotec SMCI controller.
|
||||
Mark Koennecke
|
||||
July 2015
|
||||
|
||||
Modified to use the MsgTxt field for SINQ
|
||||
|
||||
Mark Koennecke, January 2019
|
||||
*/
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
#include "SINQController.h"
|
||||
#include "SINQAxis.h"
|
||||
|
||||
#define COMLEN 80
|
||||
#define MAXMOT 99
|
||||
|
||||
class NanotecAxis : public asynMotorAxis
|
||||
class NanotecAxis : public SINQAxis
|
||||
{
|
||||
public:
|
||||
/* These are the methods we override from the base class */
|
||||
@ -38,7 +41,7 @@ private:
|
||||
friend class NanotecController;
|
||||
};
|
||||
|
||||
class NanotecController : public asynMotorController {
|
||||
class NanotecController : public SINQController {
|
||||
public:
|
||||
NanotecController(const char *portName, const char *NanotecPortName, int numMot, const char *busAddresses);
|
||||
|
||||
@ -50,7 +53,7 @@ friend class NanotecAxis;
|
||||
private:
|
||||
asynUser *pasynUserController_;
|
||||
|
||||
asynStatus transactController(char command[COMLEN], char reply[COMLEN]);
|
||||
asynStatus transactController(int axisNo, char command[COMLEN], char reply[COMLEN]);
|
||||
|
||||
|
||||
|
||||
|
@ -23,6 +23,12 @@ Though this driver has been written in 2016, the MCC-2 version used is probably
|
||||
Mark Koennecke
|
||||
September 2016
|
||||
|
||||
Updated to use the new MsgTxt field through SINQAxis
|
||||
Added a selector to support multiple phytrons on a connection
|
||||
|
||||
Mark Koennecke
|
||||
January 2019
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@ -49,19 +55,16 @@ September 2016
|
||||
* \param[in] PhytronPortName The name of the drvAsynSerialPort that was created previously to connect to the Phytron controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
PhytronController::PhytronController(const char *portName, const char *PhytronPortName, int encX, int encY)
|
||||
: asynMotorController(portName, 3, 0,
|
||||
0, // No additional interfaces beyond those in base class
|
||||
0, // No additional callback interfaces beyond those in base class
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
1, // autoconnect
|
||||
0, 0) // Default priority and stack size
|
||||
PhytronController::PhytronController(const char *portName, const char *PhytronPortName, const char *sel ,
|
||||
int encX, int encY)
|
||||
: SINQController(portName, PhytronPortName,2)
|
||||
{
|
||||
asynStatus status;
|
||||
PhytronAxis *pAxis;
|
||||
static const char *functionName = "PhytronController::PhytronController";
|
||||
char etx[2];
|
||||
|
||||
selector = strdup(sel);
|
||||
|
||||
/* Connect to Phytron controller */
|
||||
status = pasynOctetSyncIO->connect(PhytronPortName, 0, &pasynUserController_, NULL);
|
||||
if (status) {
|
||||
@ -74,12 +77,27 @@ PhytronController::PhytronController(const char *portName, const char *PhytronPo
|
||||
pasynOctetSyncIO->setOutputEos(pasynUserController_,etx,strlen(etx));
|
||||
pasynOctetSyncIO->setInputEos(pasynUserController_,etx,strlen(etx));
|
||||
|
||||
pAxis = new PhytronAxis(this, 1, encX);
|
||||
pAxis = new PhytronAxis(this, 2, encY);
|
||||
/* The special selector D selects the dose controlled axis version */
|
||||
if(strcmp(sel, (const char *)"D") == 0) {
|
||||
new PhytronDoseAxis(this, 1, encX);
|
||||
new PhytronDoseAxis(this, 2, encY);
|
||||
} else {
|
||||
new PhytronAxis(this, 1, encX);
|
||||
new PhytronAxis(this, 2, encY);
|
||||
}
|
||||
|
||||
|
||||
startPoller(1000./1000., IDLEPOLL, 2);
|
||||
}
|
||||
|
||||
PhytronDoseController::PhytronDoseController(const char *portName, const char *PhytronPortName, const char *sel ,
|
||||
int encX, int encY)
|
||||
: PhytronController(portName, PhytronPortName, sel, encX, encY)
|
||||
{
|
||||
new PhytronDoseAxis(this, 1, encX);
|
||||
new PhytronDoseAxis(this, 2, encY);
|
||||
}
|
||||
|
||||
|
||||
/** Creates a new PhytronController object.
|
||||
* Configuration command, called directly or from iocsh
|
||||
@ -87,14 +105,21 @@ PhytronController::PhytronController(const char *portName, const char *PhytronPo
|
||||
* \param[in] PhytronPortName The name of the drvAsynIPPPort that was created previously to connect to the Phytron controller
|
||||
* \param[in] numAxes The number of axes that this controller supports
|
||||
*/
|
||||
extern "C" int PhytronCreateController(const char *portName, const char *PhytronPortName, int encX, int encY)
|
||||
extern "C" int PhytronCreateController(const char *portName, const char *PhytronPortName, const char *selector,
|
||||
int encX, int encY)
|
||||
{
|
||||
PhytronController *pPhytronController
|
||||
= new PhytronController(portName, PhytronPortName, encX, encY);
|
||||
pPhytronController = NULL;
|
||||
new PhytronController(portName, PhytronPortName,selector, encX, encY);
|
||||
return(asynSuccess);
|
||||
}
|
||||
|
||||
extern "C" int PhytronDoseCreateController(const char *portName, const char *PhytronPortName, const char *selector,
|
||||
int encX, int encY)
|
||||
{
|
||||
new PhytronDoseController(portName, PhytronPortName,selector, encX, encY);
|
||||
return(asynSuccess);
|
||||
}
|
||||
|
||||
|
||||
/** Reports on status of the driver
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
* \param[in] level The level of report detail desired
|
||||
@ -127,6 +152,23 @@ PhytronAxis* PhytronController::getAxis(int axisNo)
|
||||
return static_cast<PhytronAxis*>(asynMotorController::getAxis(axisNo));
|
||||
}
|
||||
|
||||
/** Returns a pointer to an PhytronDoseAxis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] pasynUser asynUser structure that encodes the axis index number. */
|
||||
PhytronDoseAxis* PhytronDoseController::getAxis(asynUser *pasynUser)
|
||||
{
|
||||
return static_cast<PhytronDoseAxis*>(asynMotorController::getAxis(pasynUser));
|
||||
}
|
||||
|
||||
/** Returns a pointer to an PhytronDoseAxis object.
|
||||
* Returns NULL if the axis number encoded in pasynUser is invalid.
|
||||
* \param[in] axisNo Axis index number. */
|
||||
PhytronDoseAxis* PhytronDoseController::getAxis(int axisNo)
|
||||
{
|
||||
return static_cast<PhytronDoseAxis*>(asynMotorController::getAxis(axisNo));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* send a command to the Phytron and read the reply. Do some error and controller
|
||||
* issue fixing on the way
|
||||
@ -134,14 +176,16 @@ PhytronAxis* PhytronController::getAxis(int axisNo)
|
||||
* \param[out] reply The controllers reply
|
||||
*/
|
||||
|
||||
asynStatus PhytronController::transactController(char command[COMLEN], char reply[COMLEN])
|
||||
asynStatus PhytronController::transactController(int axisNo,char command[COMLEN], char reply[COMLEN])
|
||||
{
|
||||
asynStatus status;
|
||||
size_t in, out;
|
||||
int reason;
|
||||
char myReply[COMLEN+10], myCommand[COMLEN+10], *pPtr;
|
||||
SINQAxis *axis = getAxis(axisNo);
|
||||
|
||||
pasynOctetSyncIO->flush(pasynUserController_);
|
||||
|
||||
/* pasynOctetSyncIO->flush(pasynUserController_); */
|
||||
|
||||
|
||||
memset(myCommand,0,sizeof(myCommand));
|
||||
@ -155,6 +199,9 @@ asynStatus PhytronController::transactController(char command[COMLEN], char repl
|
||||
status = pasynOctetSyncIO->writeRead(pasynUserController_, myCommand, strlen(myCommand),
|
||||
myReply,sizeof(myReply), 1.,&out,&in,&reason);
|
||||
if(status != asynSuccess){
|
||||
if(axis!= NULL){
|
||||
axis->updateMsgTxtFromDriver("Lost connection to motor controller");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -176,7 +223,7 @@ asynStatus PhytronController::transactController(char command[COMLEN], char repl
|
||||
I may need to replace the ETX. But I am not sure if asyn did
|
||||
not remove it for me.
|
||||
*/
|
||||
strncat(reply,pPtr,sizeof(reply));
|
||||
strncat(reply,pPtr,COMLEN-1);
|
||||
|
||||
|
||||
return status;
|
||||
@ -191,7 +238,7 @@ asynStatus PhytronController::transactController(char command[COMLEN], char repl
|
||||
* Initializes register numbers, etc.
|
||||
*/
|
||||
PhytronAxis::PhytronAxis(PhytronController *pC, int axisNo, int enc)
|
||||
: asynMotorAxis(pC, axisNo),
|
||||
: SINQAxis(pC, axisNo),
|
||||
pC_(pC)
|
||||
{
|
||||
encoder = enc;
|
||||
@ -200,10 +247,21 @@ PhytronAxis::PhytronAxis(PhytronController *pC, int axisNo, int enc)
|
||||
} else {
|
||||
phytronChar = 'Y';
|
||||
}
|
||||
|
||||
haveBrake = 0;
|
||||
brakeIO = -1;
|
||||
next_poll = -1;
|
||||
}
|
||||
|
||||
|
||||
int PhytronAxis::setBrake(int brakeNO)
|
||||
{
|
||||
if(brakeNO < 1 || brakeNO > 8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
haveBrake = 1;
|
||||
brakeIO = brakeNO;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Reports on status of the axis
|
||||
* \param[in] fp The file pointer on which report information will be written
|
||||
@ -229,15 +287,46 @@ asynStatus PhytronAxis::move(double position, int relative, double minVelocity,
|
||||
//static const char *functionName = "PhytronAxis::move";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
/*
|
||||
deal with brake
|
||||
*/
|
||||
if(haveBrake) {
|
||||
sprintf(command,"%sA%dS", pC_->selector, brakeIO);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Failed to release brake on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Failed to release brake");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
set speed
|
||||
*/
|
||||
sprintf(command, "%s%cP14S%f", pC_->selector, phytronChar, maxVelocity);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Speed not accepted on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Invalid speed");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/*
|
||||
actually send a move command
|
||||
*/
|
||||
if (relative) {
|
||||
position += this->position;
|
||||
}
|
||||
homing = 0;
|
||||
sprintf(command, "0%cA%f", phytronChar,position/1000.);
|
||||
status = pC_->transactController(command,reply);
|
||||
sprintf(command, "%s%cA%f", pC_->selector,phytronChar,position/1000.);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Drive command not acknowledged on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Drive command not acknowledged");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
@ -251,13 +340,47 @@ asynStatus PhytronAxis::home(double minVelocity, double maxVelocity, double acce
|
||||
//static const char *functionName = "PhytronAxis::home";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
sprintf(command, "0%cO-",phytronChar);
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
/*
|
||||
handle the fucking brake
|
||||
*/
|
||||
if(haveBrake) {
|
||||
sprintf(command,"%sA%dS", pC_->selector, brakeIO);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Failed to release brake on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Failed to release brake");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
set speed
|
||||
*/
|
||||
sprintf(command, "%s%cP14S%f", pC_->selector, phytronChar, maxVelocity);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Speed not accepted on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Invalid speed");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
homing_direction = forwards;
|
||||
if(forwards){
|
||||
sprintf(command, "%s%c0+",pC_->selector,phytronChar);
|
||||
} else {
|
||||
sprintf(command, "%s%c0-",pC_->selector,phytronChar);
|
||||
}
|
||||
homing = 1;
|
||||
next_poll= -1;
|
||||
status = pC_->transactController(command,reply);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Home command not acknowledged on %d", axisNo_);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("Home command not acknowledged");
|
||||
return asynError;
|
||||
}
|
||||
return status;
|
||||
@ -274,6 +397,7 @@ asynStatus PhytronAxis::moveVelocity(double minVelocity, double maxVelocity, dou
|
||||
// "%s: minVelocity=%f, maxVelocity=%f, acceleration=%f\n",
|
||||
// functionName, minVelocity, maxVelocity, acceleration);
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
|
||||
if (maxVelocity > 0.) {
|
||||
@ -294,9 +418,10 @@ asynStatus PhytronAxis::stop(double acceleration )
|
||||
//static const char *functionName = "PhytronAxis::stop";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
sprintf(command, "0%cSN", phytronChar);
|
||||
status = pC_->transactController(command,reply);
|
||||
sprintf(command, "%s%cSN", pC_->selector,phytronChar);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
errlogPrintf("Sent STOP on Axis %d\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Axis interrupted");
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -307,10 +432,12 @@ asynStatus PhytronAxis::setPosition(double position)
|
||||
//static const char *functionName = "PhytronAxis::setPosition";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
|
||||
sprintf(command, "0%cP22S%f", phytronChar, position/1000.);
|
||||
status = pC_->transactController(command,reply);
|
||||
sprintf(command, "0%cP20S%f", phytronChar, position/1000.);
|
||||
status = pC_->transactController(command,reply);
|
||||
errlogPrintf("PhytronAxis::setPosition called with %lf\n", position);
|
||||
|
||||
sprintf(command, "%s%cP22S%f", pC_->selector,phytronChar, position/1000.);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
sprintf(command, "%s%cP20S%f", pC_->selector,phytronChar, position/1000.);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
next_poll = -1;
|
||||
|
||||
return status;
|
||||
@ -336,7 +463,7 @@ asynStatus PhytronAxis::setClosedLoop(bool closedLoop)
|
||||
* \param[out] moving A flag that is set indicating that the axis is moving (true) or done (false). */
|
||||
asynStatus PhytronAxis::poll(bool *moving)
|
||||
{
|
||||
asynStatus comStatus;
|
||||
asynStatus comStatus = asynSuccess;
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
double lowlim;
|
||||
|
||||
@ -347,19 +474,24 @@ asynStatus PhytronAxis::poll(bool *moving)
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
// Read the current motor position
|
||||
if(encoder) {
|
||||
sprintf(command,"0%cP22R",phytronChar);
|
||||
sprintf(command,"%s%cP22R",pC_->selector,phytronChar);
|
||||
} else {
|
||||
sprintf(command,"0%cP20R",phytronChar);
|
||||
sprintf(command,"%s%cP20R",pC_->selector,phytronChar);
|
||||
}
|
||||
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
if(comStatus) goto skip;
|
||||
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError) {
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("No connection to phytron controller");
|
||||
goto skip;
|
||||
}
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Bad reply for position on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Bad reply reading position");
|
||||
goto skip;
|
||||
}
|
||||
/*
|
||||
@ -372,9 +504,13 @@ asynStatus PhytronAxis::poll(bool *moving)
|
||||
|
||||
|
||||
// Read the moving status of this motor
|
||||
sprintf(command,"0%c=H",phytronChar);
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
if(comStatus) goto skip;
|
||||
sprintf(command,"%s%c=H",pC_->selector,phytronChar);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("No connection to phytron controller");
|
||||
goto skip;
|
||||
}
|
||||
/* errlogPrintf("Axis %d, status reply %s, position %lf\n", axisNo_, reply, position); */
|
||||
if(strstr(reply,"ACKN") != NULL){
|
||||
*moving = true;
|
||||
@ -384,23 +520,45 @@ asynStatus PhytronAxis::poll(bool *moving)
|
||||
*moving = false;
|
||||
next_poll = time(NULL)+IDLEPOLL;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
|
||||
if(haveBrake) {
|
||||
sprintf(command,"%sA%dR", pC_->selector, brakeIO);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Failed to set brake on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Failed to set brake");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!*moving) {
|
||||
if(homing){
|
||||
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&lowlim);
|
||||
if(homing_direction) {
|
||||
pC_->getDoubleParam(axisNo_,pC_->motorHighLimit_,&lowlim);
|
||||
} else {
|
||||
pC_->getDoubleParam(axisNo_,pC_->motorLowLimit_,&lowlim);
|
||||
}
|
||||
setPosition(lowlim);
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
} else {
|
||||
/*
|
||||
check limits and errors, upper
|
||||
*/
|
||||
sprintf(command,"0%c=I+",phytronChar);
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
if(comStatus) goto skip;
|
||||
sprintf(command,"%s%c=I+",pC_->selector,phytronChar);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError) {
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("No connection to phytron controller");
|
||||
goto skip;
|
||||
}
|
||||
if(strstr(reply,"ACKE") != NULL){
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
updateMsgTxtFromDriver("Hit High Limit");
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, false);
|
||||
}
|
||||
@ -408,11 +566,18 @@ asynStatus PhytronAxis::poll(bool *moving)
|
||||
/*
|
||||
lower limit
|
||||
*/
|
||||
sprintf(command,"0%c=I-",phytronChar);
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
if(comStatus) goto skip;
|
||||
sprintf(command,"%s%c=I-",pC_->selector,phytronChar);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("No connection to phytron controller");
|
||||
goto skip;
|
||||
}
|
||||
if(strstr(reply,"ACKE") != NULL){
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
updateMsgTxtFromDriver("Low Limit Hit");
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, false);
|
||||
}
|
||||
@ -420,12 +585,19 @@ asynStatus PhytronAxis::poll(bool *moving)
|
||||
/*
|
||||
error
|
||||
*/
|
||||
sprintf(command,"0%c=E",phytronChar);
|
||||
comStatus = pC_->transactController(command,reply);
|
||||
if(comStatus) goto skip;
|
||||
sprintf(command,"%s%c=E",pC_->selector,phytronChar);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError) {
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
updateMsgTxtFromDriver("No connection to phytron controller");
|
||||
goto skip;
|
||||
}
|
||||
if(strstr(reply,"ACKE") != NULL){
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Electronics on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Electronics error");
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
} else {
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
}
|
||||
@ -434,29 +606,165 @@ asynStatus PhytronAxis::poll(bool *moving)
|
||||
|
||||
|
||||
skip:
|
||||
setIntegerParam(pC_->motorStatusProblem_, comStatus ? 1:0);
|
||||
callParamCallbacks();
|
||||
return comStatus ? asynError : asynSuccess;
|
||||
return comStatus;
|
||||
}
|
||||
|
||||
/* The special PhytronDoseAxis code used when the speed is controlled through the neutron flux
|
||||
*/
|
||||
|
||||
PhytronDoseAxis::PhytronDoseAxis(PhytronController *pC, int axisNo, int enc)
|
||||
: PhytronAxis(pC, axisNo, enc)
|
||||
{
|
||||
if(axisNo == 1){
|
||||
doseChar = '3';
|
||||
} else {
|
||||
doseChar = '4';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asynStatus PhytronDoseAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
|
||||
{
|
||||
asynStatus status;
|
||||
//static const char *functionName = "PhytronDoseAxis::move";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
float realTarget;
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
/*
|
||||
set conversion factor from neutron rate to speed
|
||||
*/
|
||||
sprintf(command, "R%c3S%f", doseChar, maxVelocity);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Speed not accepted on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Invalid speed");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/*
|
||||
actually send a move command
|
||||
*/
|
||||
if (relative) {
|
||||
position += this->position;
|
||||
}
|
||||
homing = 0;
|
||||
|
||||
/* set target */
|
||||
realTarget = position/1000.;
|
||||
if(realTarget > this->position) {
|
||||
sprintf(command, "R%c1S%f", doseChar,realTarget);
|
||||
} else {
|
||||
sprintf(command, "R%c2S%f", doseChar,realTarget);
|
||||
}
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Drive command not acknowledged on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Drive command not acknowledged");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/* really start the move */
|
||||
sprintf(command, "R%c01S", doseChar);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
if(strstr(reply,"NACK") != NULL){
|
||||
errlogSevPrintf(errlogMajor, "Drive start command not acknowledged on %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Drive command not acknowledged");
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
next_poll = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
extern "C" asynStatus PhytronConfBrake(const char *port, /* specify which controller by port name */
|
||||
int axis, /* axis: 0, 1 */
|
||||
int brakeNO) /* brakeIO No, 1-8 */
|
||||
{
|
||||
PhytronController *pC;
|
||||
PhytronAxis *pAxis;
|
||||
int status;
|
||||
|
||||
static const char *functionName = "PhytronConfBrake";
|
||||
|
||||
pC = (PhytronController*) findAsynPortDriver(port);
|
||||
if (!pC) {
|
||||
printf("%s:%s: Error port %s not found\n",
|
||||
"PhytronDriver", functionName, port);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
pC->lock();
|
||||
pAxis = pC->getAxis(axis);
|
||||
status = pAxis->setBrake(brakeNO);
|
||||
pC->unlock();
|
||||
if(!status) {
|
||||
printf("%s:%s:%s requested brake IO out of range\n",
|
||||
"PhytronDriver", functionName, port);
|
||||
return asynError;
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/** Code for iocsh registration */
|
||||
static const iocshArg PhytronCreateControllerArg0 = {"Port name", iocshArgString};
|
||||
static const iocshArg PhytronCreateControllerArg1 = {"Phytron port name", iocshArgString};
|
||||
static const iocshArg PhytronCreateControllerArg2 = {"EnoderX", iocshArgInt};
|
||||
static const iocshArg PhytronCreateControllerArg3 = {"EnoderY", iocshArgInt};
|
||||
static const iocshArg PhytronCreateControllerArg2 = {"Phytron Selector", iocshArgString};
|
||||
static const iocshArg PhytronCreateControllerArg3 = {"EncoderX", iocshArgInt};
|
||||
static const iocshArg PhytronCreateControllerArg4 = {"EncoderY", iocshArgInt};
|
||||
static const iocshArg * const PhytronCreateControllerArgs[] = {&PhytronCreateControllerArg0,
|
||||
&PhytronCreateControllerArg1,
|
||||
&PhytronCreateControllerArg2,
|
||||
&PhytronCreateControllerArg3};
|
||||
static const iocshFuncDef PhytronCreateControllerDef = {"PhytronCreateController", 4, PhytronCreateControllerArgs};
|
||||
&PhytronCreateControllerArg3,
|
||||
&PhytronCreateControllerArg4};
|
||||
static const iocshFuncDef PhytronCreateControllerDef = {"PhytronCreateController", 5, PhytronCreateControllerArgs};
|
||||
static void PhytronCreateContollerCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
PhytronCreateController(args[0].sval, args[1].sval, args[2].ival,args[3].ival);
|
||||
PhytronCreateController(args[0].sval, args[1].sval, args[2].sval, args[3].ival,args[4].ival);
|
||||
}
|
||||
|
||||
static const iocshArg PhytronDoseCreateControllerArg0 = {"Port name", iocshArgString};
|
||||
static const iocshArg PhytronDoseCreateControllerArg1 = {"Phytron port name", iocshArgString};
|
||||
static const iocshArg PhytronDoseCreateControllerArg2 = {"Phytron Selector", iocshArgString};
|
||||
static const iocshArg PhytronDoseCreateControllerArg3 = {"EncoderX", iocshArgInt};
|
||||
static const iocshArg PhytronDoseCreateControllerArg4 = {"EncoderY", iocshArgInt};
|
||||
static const iocshArg * const PhytronDoseCreateControllerArgs[] = {&PhytronDoseCreateControllerArg0,
|
||||
&PhytronDoseCreateControllerArg1,
|
||||
&PhytronDoseCreateControllerArg2,
|
||||
&PhytronDoseCreateControllerArg3,
|
||||
&PhytronDoseCreateControllerArg4};
|
||||
static const iocshFuncDef PhytronDoseCreateControllerDef = {"PhytronDoseCreateController", 5, PhytronDoseCreateControllerArgs};
|
||||
static void PhytronDoseCreateContollerCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
PhytronDoseCreateController(args[0].sval, args[1].sval, args[2].sval, args[3].ival,args[4].ival);
|
||||
}
|
||||
|
||||
/* PhytronConfigureBrake */
|
||||
static const iocshArg phytronBrakeArg0 = {"Controller port name", iocshArgString};
|
||||
static const iocshArg phytronBrakeArg1 = {"Axis number", iocshArgInt};
|
||||
static const iocshArg phytronBrakeArg2 = {"Brake IO number", iocshArgInt};
|
||||
static const iocshArg * const phytronBrakeArgs[] = {&phytronBrakeArg0,
|
||||
&phytronBrakeArg1,
|
||||
&phytronBrakeArg2};
|
||||
static const iocshFuncDef phytronBrakeDef = {"PhytronConfigureBrake", 3, phytronBrakeArgs};
|
||||
|
||||
static void phytronBrakeCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
PhytronConfBrake(args[0].sval, args[1].ival, args[2].ival);
|
||||
}
|
||||
|
||||
|
||||
static void PhytronRegister(void)
|
||||
{
|
||||
iocshRegister(&PhytronCreateControllerDef, PhytronCreateContollerCallFunc);
|
||||
iocshRegister(&PhytronDoseCreateControllerDef, PhytronDoseCreateContollerCallFunc);
|
||||
iocshRegister(&phytronBrakeDef, phytronBrakeCallFunc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
@ -5,14 +5,28 @@ USAGE... Motor driver support for the Phytron MCC-2 motor controller.
|
||||
Mark Koennecke
|
||||
September 2016
|
||||
|
||||
|
||||
Updated to go through SINQAxis for -MsgTxt support
|
||||
Added a selector to support multiple phytrons on a connection
|
||||
|
||||
Mark Koennecke, January 2019
|
||||
|
||||
Added PhytronDoseAxis. The speed of this axis is controlled by the
|
||||
neutron dose rate fed in as analog signal
|
||||
|
||||
Mark Koennecke, April 2023
|
||||
*/
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
#include "SINQController.h"
|
||||
#include "SINQAxis.h"
|
||||
|
||||
#define COMLEN 80
|
||||
|
||||
class PhytronAxis : public asynMotorAxis
|
||||
|
||||
class PhytronController;
|
||||
class PhytronDoseController;
|
||||
|
||||
class PhytronAxis : public SINQAxis
|
||||
{
|
||||
public:
|
||||
/* These are the methods we override from the base class */
|
||||
@ -25,32 +39,71 @@ public:
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus setPosition(double position);
|
||||
asynStatus setClosedLoop(bool closedLoop);
|
||||
int setBrake(int brakeIO);
|
||||
|
||||
private:
|
||||
protected:
|
||||
char phytronChar;
|
||||
PhytronController *pC_; /**< Pointer to the asynMotorController to which this axis belongs.
|
||||
* Abbreviated because it is used very frequently */
|
||||
double position;
|
||||
int homing;
|
||||
int homing_direction; /*1 forward, 0 backwards */
|
||||
time_t next_poll;
|
||||
int encoder;
|
||||
int haveBrake;
|
||||
int brakeIO;
|
||||
|
||||
friend class PhytronController;
|
||||
};
|
||||
|
||||
class PhytronController : public asynMotorController {
|
||||
class PhytronDoseAxis : public PhytronAxis
|
||||
{
|
||||
// A special version of PhytronAxis where the speed is controlled by the neutron flux.
|
||||
public:
|
||||
PhytronController(const char *portName, const char *PhytronPortName, int encX, int encY);
|
||||
PhytronDoseAxis(class PhytronController *pC, int axis, int enc);
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
|
||||
friend class PhytronDoseController;
|
||||
|
||||
protected:
|
||||
char doseChar;
|
||||
PhytronDoseController *pC_;
|
||||
};
|
||||
|
||||
|
||||
class PhytronController : public SINQController {
|
||||
public:
|
||||
PhytronController(const char *portName, const char *PhytronPortName, const char *selector,
|
||||
int encX, int encY);
|
||||
|
||||
void report(FILE *fp, int level);
|
||||
PhytronAxis* getAxis(asynUser *pasynUser);
|
||||
PhytronAxis* getAxis(int axisNo);
|
||||
|
||||
friend class PhytronAxis;
|
||||
private:
|
||||
|
||||
protected:
|
||||
asynUser *pasynUserController_;
|
||||
|
||||
asynStatus transactController(char command[COMLEN], char reply[COMLEN]);
|
||||
asynStatus transactController(int axisNo, char command[COMLEN], char reply[COMLEN]);
|
||||
|
||||
const char *selector;
|
||||
|
||||
};
|
||||
|
||||
class PhytronDoseController : public PhytronController {
|
||||
public:
|
||||
PhytronDoseController(const char *portName, const char *PhytronPortName, const char *selector,
|
||||
int encX, int encY);
|
||||
|
||||
PhytronDoseAxis* getAxis(asynUser *pasynUser);
|
||||
PhytronDoseAxis* getAxis(int axisNo);
|
||||
|
||||
friend class PhytronDoseAxis;
|
||||
|
||||
protected:
|
||||
asynUser *pasynUserController_;
|
||||
const char *selector;
|
||||
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@ SINQAxis::SINQAxis(class SINQController *pC, int axis)
|
||||
: asynMotorAxis((asynMotorController *)pC, axis),
|
||||
pC_(pC)
|
||||
{
|
||||
updateMsgTxtFromDriver("");
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +23,7 @@ void SINQAxis::updateMsgTxtFromDriver(const char *value)
|
||||
setStringParam(pC_->motorMessageText_,value);
|
||||
} else {
|
||||
pC_->setIntegerParam(axisNo_,pC_->motorMessageIsFromDriver_, 0);
|
||||
setStringParam(pC_->motorMessageText_,"");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,19 @@
|
||||
Code lifted from Torsten Boegershausen ESS code.
|
||||
|
||||
Mark Koennecke, March 2017
|
||||
|
||||
Added code to manage an interMessageSleep
|
||||
|
||||
Mark Koennecke, February 2024
|
||||
*/
|
||||
|
||||
#include "SINQController.h"
|
||||
#include "asynMotorController.h"
|
||||
#include "epicsExport.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
SINQController::SINQController(const char *portName, const char *SINQPortName, int numAxes)
|
||||
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS+2,
|
||||
SINQController::SINQController(const char *portName, const char *SINQPortName, int numAxes, const int& extraParams)
|
||||
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS+extraParams,
|
||||
0, // No additional interfaces beyond those in base class
|
||||
0, // No additional callback interfaces beyond those in base class
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
@ -20,6 +26,53 @@ SINQController::SINQController(const char *portName, const char *SINQPortName, i
|
||||
{
|
||||
createParam(motorMessageIsFromDriverString, asynParamInt32, &motorMessageIsFromDriver_);
|
||||
createParam(motorMessageTextString, asynParamOctet, &motorMessageText_);
|
||||
|
||||
interMessageSleep=20;
|
||||
}
|
||||
|
||||
/** Set the interMessageSleep at runtime */
|
||||
asynStatus SINQController::setInterMessageSleep(int messageSleep)
|
||||
{
|
||||
lock();
|
||||
interMessageSleep = messageSleep;
|
||||
unlock();
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
asynStatus setInterMessageSleep(const char *portName, int messageSleep)
|
||||
{
|
||||
SINQController *pC;
|
||||
static const char *functionName = "setIntermessageSleep";
|
||||
|
||||
pC = (SINQController*) findAsynPortDriver(portName);
|
||||
if (!pC) {
|
||||
printf("%s:%s: Error port %s not found\n", "SINQController", functionName, portName);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
return pC->setInterMessageSleep(messageSleep);
|
||||
}
|
||||
|
||||
|
||||
/* setInterMessageSleep */
|
||||
static const iocshArg setInterMessageSleepArg0 = {"Controller port name", iocshArgString};
|
||||
static const iocshArg setInterMessageSleepArg1 = {"inter message sleep time", iocshArgInt};
|
||||
static const iocshArg * const setInterMessageSleepArgs[] = {&setInterMessageSleepArg0,
|
||||
&setInterMessageSleepArg1};
|
||||
static const iocshFuncDef setInterMessageSleepDef = {"setInterMessageSleep", 2, setInterMessageSleepArgs};
|
||||
|
||||
static void setInterMessageSleepCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
setInterMessageSleep(args[0].sval, args[1].ival);
|
||||
}
|
||||
|
||||
static void SINQControllerRegister(void)
|
||||
{
|
||||
iocshRegister(&setInterMessageSleepDef, setInterMessageSleepCallFunc);
|
||||
}
|
||||
epicsExportRegistrar(SINQControllerRegister);
|
||||
|
||||
} // extern C
|
||||
|
||||
|
@ -17,12 +17,16 @@
|
||||
class epicsShareClass SINQController : public asynMotorController
|
||||
{
|
||||
public:
|
||||
SINQController(const char *portName, const char *SINQPortName, int numAxes);
|
||||
|
||||
SINQController(const char *portName, const char *SINQPortName, int numAxes, const int& extraParams=2);
|
||||
asynStatus setInterMessageSleep(int messageSleep);
|
||||
|
||||
friend class SINQAxis;
|
||||
protected:
|
||||
int motorMessageIsFromDriver_;
|
||||
int motorMessageText_;
|
||||
|
||||
int interMessageSleep; // minimum time between message to the controller in microseconds
|
||||
// for slowing down communication to weak controllers.....
|
||||
};
|
||||
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
*
|
||||
* Mark Koennecke, February 2013
|
||||
*
|
||||
* Enhanced by adding an addtional external pause and status flag in the databse and code in here
|
||||
* Enhanced by adding an addtional external pause and status flag in the database and code in here
|
||||
* to handle this. Moreover an external MsgTxt field in the DB can be filled with an error message.
|
||||
*
|
||||
* Mark Koennecke, July 2017
|
||||
@ -40,6 +40,8 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <asynOctetSyncIO.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsThread.h>
|
||||
@ -112,13 +114,13 @@ typedef struct {
|
||||
unsigned int dbInit;
|
||||
}EL737priv;
|
||||
|
||||
static void dummyAsynCallback(asynUser *pasynUser)
|
||||
static void dummyAsynCallback([[maybe_unused]] asynUser *pasynUser)
|
||||
{
|
||||
}
|
||||
|
||||
static void connectSlaveRecords(EL737priv *priv)
|
||||
{
|
||||
char slaveName[PVNAME_SZ], errName[256];
|
||||
char slaveName[PVNAME_SZ + 18], errName[256];
|
||||
long status;
|
||||
|
||||
priv->dbInit = 1;
|
||||
@ -163,10 +165,10 @@ static void connectSlaveRecords(EL737priv *priv)
|
||||
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
|
||||
priv->dbInit = 0;
|
||||
} else {
|
||||
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->status.precord->name);
|
||||
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->threshCounter.precord->name);
|
||||
}
|
||||
|
||||
snprintf(slaveName,sizeof(slaveName),"%s:Threshold", priv->psr->name);
|
||||
snprintf(slaveName, sizeof(slaveName), "%s:Threshold", priv->psr->name);
|
||||
errlogPrintf("Name of thresholdCounter variable: %s\n", slaveName);
|
||||
status = dbNameToAddr(slaveName,&priv->threshold);
|
||||
if(status!= 0){
|
||||
@ -174,9 +176,9 @@ static void connectSlaveRecords(EL737priv *priv)
|
||||
errlogPrintf("dbNameToAddr failed for %s with %s\n", slaveName, errName);
|
||||
priv->dbInit = 0;
|
||||
} else {
|
||||
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->status.precord->name);
|
||||
errlogPrintf("dbNameToAddr succeded for %s, record access %s\n",slaveName, priv->threshold.precord->name);
|
||||
}
|
||||
|
||||
priv->thresholdValue = -999;
|
||||
}
|
||||
|
||||
static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
|
||||
@ -222,17 +224,18 @@ static long el737_init_record(scalerRecord *psr, CALLBACK *pcallback)
|
||||
psr->pact = 1;
|
||||
return 0;
|
||||
}
|
||||
pasynManager->autoConnect(dummyUser, 1);
|
||||
status = pasynOctetSyncIO->connect(port, 0, &priv->asynContext, NULL);
|
||||
if (status) {
|
||||
asynPrint(dummyUser, ASYN_TRACE_ERROR,
|
||||
"%s: cannot connect to EL734 controller\n",
|
||||
"%s: cannot connect to EL737 controller\n",
|
||||
"el737_init_scaler");
|
||||
psr->pact = 1;
|
||||
return 0;
|
||||
}
|
||||
pasynOctetSyncIO->setOutputEos(priv->asynContext,"\r",strlen("\r"));
|
||||
pasynOctetSyncIO->setInputEos(priv->asynContext,"\r",strlen("\r"));
|
||||
priv->asynContext->timeout = 1.0;
|
||||
priv->asynContext->timeout = 2.0;
|
||||
strcpy(command,"RMT 1");
|
||||
pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
|
||||
reply,sizeof(reply), 1.,&out,&in,&reason);
|
||||
@ -287,7 +290,7 @@ static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val)
|
||||
{
|
||||
EL737priv *priv;
|
||||
|
||||
//errlogPrintf("Setting preset %d to %ld\n", signal, val);
|
||||
// errlogPrintf("EL737: Setting preset %d to %ld\n", signal, val);
|
||||
|
||||
priv = (EL737priv *)psr->dpvt;
|
||||
if(signal >= 0 && signal < 13){
|
||||
@ -296,7 +299,7 @@ static long el737_write_preset(scalerRecord *psr, int signal, unsigned long val)
|
||||
if(signal == THRESHVAL){
|
||||
priv->sendThreshold = 1;
|
||||
epicsEventSignal(priv->wakeUp);
|
||||
//errlogPrintf("Setting threshold \n");
|
||||
// errlogPrintf("EL737: Setting threshold \n");
|
||||
|
||||
}
|
||||
return 0;
|
||||
@ -309,7 +312,7 @@ static long el737_arm(scalerRecord *psr, int val)
|
||||
priv = (EL737priv *)psr->dpvt;
|
||||
priv->countCommand = val;
|
||||
|
||||
//errlogPrintf("Sending arm %d \n", val);
|
||||
// errlogPrintf("EL737: Sending arm %d \n", val);
|
||||
|
||||
epicsEventSignal(priv->wakeUp);
|
||||
|
||||
@ -321,7 +324,7 @@ static long el737_done(scalerRecord *psr)
|
||||
EL737priv *priv;
|
||||
priv = (EL737priv *)psr->dpvt;
|
||||
|
||||
// errlogPrintf("Calling done with %d\n", psr->cnt);
|
||||
// errlogPrintf("EL737: Calling done with %d\n", psr->cnt);
|
||||
if(priv->counting && psr->cnt == 0){
|
||||
priv->countCommand = 0;
|
||||
epicsEventSignal(priv->wakeUp);
|
||||
@ -343,7 +346,9 @@ static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],cha
|
||||
|
||||
pasynOctetSyncIO->flush(priv->asynContext);
|
||||
|
||||
strcpy(message,"OK");
|
||||
// errlogPrintf("EL737: sending command %s\n", command);
|
||||
|
||||
strcpy(message,"");
|
||||
status = pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
if(status == asynSuccess){
|
||||
@ -384,7 +389,13 @@ static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],cha
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strncpy(message,"Lost communication",sizeof(message));
|
||||
if(errno == EAGAIN){
|
||||
errlogPrintf("Lost response to %s with EAGAIN\n", command);
|
||||
} else {
|
||||
snprintf(message,sizeof(message), "Lost communication with errno %d", errno);
|
||||
/* force a reconnect */
|
||||
pasynOctetSyncIO->disconnect(priv->asynContext);
|
||||
}
|
||||
}
|
||||
if(priv->dbInit){
|
||||
dbStatus = dbPutField(&priv->msgTxt, DBR_STRING,message, 1);
|
||||
@ -399,7 +410,7 @@ static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],cha
|
||||
static asynStatus sendStop(EL737priv *priv)
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
//errlogPrintf("Sending stop\n");
|
||||
// errlogPrintf("EL737: Sending stop\n");
|
||||
strcpy(command,"S");
|
||||
return el737_transactCommand(priv,command,reply);
|
||||
}
|
||||
@ -408,13 +419,14 @@ static void runEvents(EL737priv *priv)
|
||||
{
|
||||
char command[COMLEN], reply[COMLEN], errName[256];
|
||||
int status;
|
||||
long dbStatus, myThreshold, nElements = 1, options = 0, threshCounter;
|
||||
long dbStatus, nElements = 1, options = 0, threshCounter;
|
||||
long unsigned int myThreshold;
|
||||
|
||||
/*
|
||||
This is the better way to set the threshold rather then the old way below which uses
|
||||
presets for that function. This one uses separate fields.
|
||||
*/
|
||||
if(priv->dbInit == 1) {
|
||||
if(priv->dbInit == 1 && priv->sendThreshold) {
|
||||
dbStatus = dbGetField(&priv->threshold,DBR_LONG,&myThreshold,&options, &nElements,NULL);
|
||||
if(dbStatus != 0){
|
||||
errSymLookup(dbStatus,errName,sizeof(errName));
|
||||
@ -425,13 +437,16 @@ static void runEvents(EL737priv *priv)
|
||||
we have to set the threshold
|
||||
*/
|
||||
dbStatus = dbGetField(&priv->threshCounter,DBR_LONG,&threshCounter, &options, &nElements,NULL);
|
||||
if(dbStatus != 0){
|
||||
if(dbStatus != 0) {
|
||||
errSymLookup(dbStatus,errName,sizeof(errName));
|
||||
errlogPrintf("Reading thresholdCounter failed with %s\n", errName);
|
||||
} else {
|
||||
if(threshCounter == 0){
|
||||
threshCounter = 1;
|
||||
}
|
||||
sprintf(command,"DL %d %d", (int)threshCounter,
|
||||
(int)myThreshold);
|
||||
//errlogPrintf("Sending threshold command %s\n", command);
|
||||
// errlogPrintf("Sending threshold command %s\n", command);
|
||||
status = el737_transactCommand(priv,command,reply);
|
||||
sprintf(command,"DR %d", (int)threshCounter);
|
||||
status = el737_transactCommand(priv, command,reply);
|
||||
@ -442,35 +457,36 @@ static void runEvents(EL737priv *priv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(priv->sendThreshold == 1){
|
||||
sprintf(command,"DL %d %d", (int)priv->presets[THRESHMON],
|
||||
(int)priv->presets[THRESHVAL]);
|
||||
//errlogPrintf("Sending threshold command %s\n", command);
|
||||
status = el737_transactCommand(priv,command,reply);
|
||||
sprintf(command,"DR %d", (int)priv->presets[THRESHMON]);
|
||||
status = el737_transactCommand(priv, command,reply);
|
||||
if(status == asynSuccess){
|
||||
priv->sendThreshold = 0;
|
||||
if(priv->sendThreshold == 1) {
|
||||
// fallback when we do not have the DB fields or threshCounter is invalid
|
||||
if(priv->presets[THRESHMON] > 0) {
|
||||
sprintf(command,"DL %d %d", (int)priv->presets[THRESHMON],
|
||||
(int)priv->presets[THRESHVAL]);
|
||||
// errlogPrintf("Sending threshold from presets, command %s\n", command);
|
||||
status = el737_transactCommand(priv,command,reply);
|
||||
sprintf(command,"DR %d", (int)priv->presets[THRESHMON]);
|
||||
status = el737_transactCommand(priv, command,reply);
|
||||
if(status == asynSuccess) {
|
||||
priv->sendThreshold = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//errlogPrintf("Doing a count command: %d\n", priv->countCommand);
|
||||
errlogPrintf("EL737: Doing a count command: %d\n", priv->countCommand);
|
||||
if(priv->countCommand == 1){
|
||||
if(priv->presets[MODE] > 0) {
|
||||
/* preset monitor */
|
||||
sprintf(command,"MP %d", (int)priv->presets[MONITOR]);
|
||||
//errlogPrintf("Starting preset monitor\n");
|
||||
// errlogPrintf("EL737: Starting preset monitor\n");
|
||||
} else {
|
||||
/* preset time */
|
||||
sprintf(command,"TP %f", priv->presets[TIMER]/1000.);
|
||||
//errlogPrintf("Starting preset timer\n");
|
||||
// errlogPrintf("EL737: Starting preset timer\n");
|
||||
}
|
||||
status = el737_transactCommand(priv,command,reply);
|
||||
if(status == asynSuccess){
|
||||
priv->counting = 1;
|
||||
}
|
||||
priv->counting = 1;
|
||||
} else {
|
||||
if(priv->counting) {
|
||||
/* Stop */
|
||||
@ -482,8 +498,8 @@ static void runEvents(EL737priv *priv)
|
||||
|
||||
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n, errno =%d",
|
||||
priv->asynContext->errorMessage, errno);
|
||||
}
|
||||
}
|
||||
|
||||
@ -497,8 +513,12 @@ static void updateValues(EL737priv *priv)
|
||||
strcpy(command,"RA");
|
||||
status = el737_transactCommand(priv,command,reply);
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
if(errno == EAGAIN){
|
||||
errlogPrintf("devScalerEL737::el737Thread lost response to %s with EAGAIN\n", command);
|
||||
} else {
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s, errno = %d\n",
|
||||
priv->asynContext->errorMessage, errno);
|
||||
}
|
||||
return;
|
||||
}
|
||||
status = sscanf(reply, "%f %ld %ld %ld %ld %ld %ld %ld %ld",
|
||||
@ -536,12 +556,11 @@ static void el737Thread(void *param)
|
||||
int rs, ctStatus;
|
||||
long dbStatus, options, nElements = 1, pauseFlag = 0;
|
||||
|
||||
//errlogPrintf("Within EL737 thread \n");
|
||||
// errlogPrintf("EL737: Within EL737 thread \n");
|
||||
|
||||
while(1){
|
||||
evStatus = epicsEventWaitWithTimeout(priv->wakeUp,timeout);
|
||||
//errlogPrintf("EL737 thread woke up with %d\n", evStatus);
|
||||
|
||||
// errlogPrintf("EL737 thread woke up with %d\n", evStatus);
|
||||
|
||||
if(evStatus == epicsEventWaitOK) {
|
||||
/*
|
||||
@ -549,7 +568,8 @@ static void el737Thread(void *param)
|
||||
*/
|
||||
runEvents(priv);
|
||||
if(priv->counting == 1){
|
||||
timeout = .1;
|
||||
timeout = .2;
|
||||
usleep(500);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@ -568,7 +588,7 @@ static void el737Thread(void *param)
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
sscanf(reply,"%d",&rs);
|
||||
//errlogPrintf("RS = %d\n", rs);
|
||||
@ -617,7 +637,7 @@ static void el737Thread(void *param)
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
} else if(pauseFlag == 0 && ctStatus == 3){
|
||||
strcpy(command,"CO");
|
||||
@ -625,7 +645,7 @@ static void el737Thread(void *param)
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,21 +7,28 @@
|
||||
* Matthew Pearson
|
||||
* 23 May 2012
|
||||
*
|
||||
* Modified to use the MsgTxt field for SINQ
|
||||
*
|
||||
* Mark Koennecke, January 2019
|
||||
*
|
||||
* EXtended with special motor axis for the Selene
|
||||
* guide, Mark Koennecke, February 2020
|
||||
********************************************/
|
||||
|
||||
#ifndef pmacAxis_H
|
||||
#define pmacAxis_H
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
#include "SINQController.h"
|
||||
#include "SINQAxis.h"
|
||||
|
||||
class pmacController;
|
||||
class SeleneController;
|
||||
|
||||
class pmacAxis : public asynMotorAxis
|
||||
class pmacAxis : public SINQAxis
|
||||
{
|
||||
public:
|
||||
/* These are the methods we override from the base class */
|
||||
pmacAxis(pmacController *pController, int axisNo);
|
||||
pmacAxis(pmacController *pController, int axisNo, bool enable=true);
|
||||
virtual ~pmacAxis();
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
|
||||
@ -29,8 +36,9 @@ class pmacAxis : public asynMotorAxis
|
||||
asynStatus stop(double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus setPosition(double position);
|
||||
|
||||
private:
|
||||
asynStatus enable(int on);
|
||||
|
||||
protected:
|
||||
pmacController *pC_;
|
||||
|
||||
asynStatus getAxisStatus(bool *moving);
|
||||
@ -53,9 +61,149 @@ class pmacAxis : public asynMotorAxis
|
||||
time_t status6Time;
|
||||
int starting;
|
||||
int homing;
|
||||
double statusPos;
|
||||
|
||||
time_t next_poll;
|
||||
|
||||
bool autoEnable;
|
||||
|
||||
friend class pmacController;
|
||||
friend class pmacV3Controller;
|
||||
};
|
||||
/*--------------------------------------------------------------------------------------------*/
|
||||
class SeleneAxis : public pmacAxis
|
||||
{
|
||||
public:
|
||||
SeleneAxis(SeleneController *pController, int axisNo, double limitTarget);
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus setPosition(double position);
|
||||
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
|
||||
|
||||
protected:
|
||||
friend class SeleneController;
|
||||
friend class pmacController;
|
||||
|
||||
private:
|
||||
double limitTarget;
|
||||
asynStatus getSeleneAxisInitialStatus(void);
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
Yet another special set of motors for the Selene Guide at AMOR. Each segment can be lifted or tilted. This is
|
||||
two motors. One acts as a slave and only writes a new target, the other also sets a new target and sends the
|
||||
actual movement command. Both motors are coordianted in the motor controller in order to avoid tension on
|
||||
the guide elements. This gaves rise to the function code LIFTSLAVE and LIFTMASTER.
|
||||
|
||||
In another mode the whole guide can be lifted or tilted. Then motor 1 and 6 get new values and one of them
|
||||
sends the drive command. This causes all 6 motors to drive synchronously to their new targets. This is
|
||||
implemented through the LIFTSEGMENT function code.
|
||||
|
||||
Mark Koennecke, February 2020
|
||||
|
||||
The axis should not be enabled automatically
|
||||
|
||||
Michele Brambilla, February 2020
|
||||
|
||||
*/
|
||||
class LiftAxis : public pmacAxis
|
||||
{
|
||||
public:
|
||||
LiftAxis(pmacController *pController, int axisNo) : pmacAxis((pmacController *)pController,axisNo) {};
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus stop(double acceleration);
|
||||
private:
|
||||
int waitStart;
|
||||
};
|
||||
|
||||
/********************************************
|
||||
* Protocol version 3 requires just some minor change
|
||||
*
|
||||
* Michele Brambilla, February 2022
|
||||
********************************************/
|
||||
|
||||
class pmacV3Axis : public pmacAxis {
|
||||
public:
|
||||
|
||||
pmacV3Axis(pmacController *pController, int axisNo);
|
||||
|
||||
asynStatus move(double position, int relative, double min_velocity,
|
||||
double max_velocity, double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
protected:
|
||||
int IsEnable;
|
||||
double Speed;
|
||||
|
||||
asynStatus getAxisStatus(bool *moving);
|
||||
|
||||
friend class pmacController;
|
||||
friend class pmacV3Controller;
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------------------------*/
|
||||
class pmacHRPTAxis : public pmacV3Axis
|
||||
{
|
||||
public:
|
||||
pmacHRPTAxis(pmacController *pController, int axisNo) : pmacV3Axis(pController,axisNo) {};
|
||||
/**
|
||||
* Override getAxisStatus in order to read the special parameter indicating a
|
||||
* slit blade crash at HRPT
|
||||
*/
|
||||
asynStatus getAxisStatus(bool *moving);
|
||||
|
||||
protected:
|
||||
friend class pmacController;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Special motors for the AMOR detector movement. The whole
|
||||
* command set is different but on a pmac controller. This implements
|
||||
* a coordinated movement of cox, coz and ftz in order not to break
|
||||
* the flight tube which may have been mounted. This is mapped to three
|
||||
* motors selected via the function code: com, the detector omega, coz,
|
||||
* the detector z offset and park, for parking the flightpath.
|
||||
*/
|
||||
|
||||
#define ADCOM 1
|
||||
#define ADCOZ 2
|
||||
#define ADPARK 3
|
||||
|
||||
class AmorDetectorAxis: public pmacAxis {
|
||||
public:
|
||||
AmorDetectorAxis(pmacController *pController, int axisNo, int function);
|
||||
|
||||
asynStatus move(double position, int relative, double min_velocity,
|
||||
double max_velocity, double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus home(double min_velocity, double max_velocity, double acceleration, int forwards);
|
||||
asynStatus stop(double acceleration);
|
||||
asynStatus setPosition(double position);
|
||||
|
||||
protected:
|
||||
int _function;
|
||||
int det_starting;
|
||||
time_t det_startTime;
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
class GirderAxis: public pmacV3Axis {
|
||||
public:
|
||||
GirderAxis(pmacController *pController, int axisNo);
|
||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
||||
asynStatus stop(double acceleration);
|
||||
asynStatus poll(bool *moving);
|
||||
|
||||
protected:
|
||||
int IsEnable;
|
||||
|
||||
friend class pmacController;
|
||||
friend class pmacV3Controller;
|
||||
};
|
||||
|
||||
#endif /* pmacAxis_H */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,23 +7,27 @@
|
||||
* Matthew Pearson
|
||||
* 23 May 2012
|
||||
*
|
||||
*
|
||||
* Modified to use the MsgTxt field for SINQ
|
||||
*
|
||||
* Mark Koennecke, January 2019
|
||||
********************************************/
|
||||
|
||||
#ifndef pmacController_H
|
||||
#define pmacController_H
|
||||
|
||||
#include "asynMotorController.h"
|
||||
#include "SINQController.h"
|
||||
#include "asynMotorAxis.h"
|
||||
#include "pmacAxis.h"
|
||||
|
||||
#define PMAC_C_GlobalStatusString "PMAC_C_GLOBALSTATUS"
|
||||
#define PMAC_C_CommsErrorString "PMAC_C_COMMSERROR"
|
||||
|
||||
class pmacController : public asynMotorController {
|
||||
class pmacController : public SINQController {
|
||||
|
||||
public:
|
||||
pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress, int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod);
|
||||
pmacController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress, int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod, const int& extraParams=2);
|
||||
|
||||
virtual ~pmacController();
|
||||
|
||||
@ -45,13 +49,13 @@ class pmacController : public asynMotorController {
|
||||
int PMAC_C_GlobalStatus_;
|
||||
int PMAC_C_CommsError_;
|
||||
#define LAST_PMAC_PARAM PMAC_C_CommsError__
|
||||
void debugFlow(const char *message);
|
||||
asynStatus lowLevelWriteRead(int axisNo, const char *command, char *response);
|
||||
|
||||
private:
|
||||
asynUser* lowLevelPortUser_;
|
||||
epicsUInt32 debugFlag_;
|
||||
asynStatus lowLevelWriteRead(const char *command, char *response);
|
||||
int lowLevelPortConnect(const char *port, int addr, asynUser **ppasynUser, char *inputEos, char *outputEos);
|
||||
void debugFlow(const char *message);
|
||||
|
||||
//static class data members
|
||||
|
||||
@ -132,9 +136,64 @@ class pmacController : public asynMotorController {
|
||||
static const epicsUInt32 PMAX_AXIS_GENERAL_PROB2;
|
||||
|
||||
friend class pmacAxis;
|
||||
|
||||
friend class pmacHRPTAxis;
|
||||
friend class SeleneAxis;
|
||||
friend class LiftAxis;
|
||||
friend class pmacV3Axis;
|
||||
friend class GirderAxis;
|
||||
friend class AmorDetectorAxis;
|
||||
};
|
||||
|
||||
#define NUM_PMAC_PARAMS (&LAST_PMAC_PARAM - &FIRST_PMAC_PARAM + 1)
|
||||
|
||||
#define MotorSetPositionString "SET_MOTOR_POSITION"
|
||||
|
||||
class SeleneController : public pmacController {
|
||||
public:
|
||||
SeleneController(const char *portName, const char *lowLevelPortName, int lowLevelPortAddress,
|
||||
int numAxes, double movingPollPeriod, double idlePollPeriod);
|
||||
|
||||
~SeleneController(void) { }
|
||||
|
||||
// overloaded because we have a different command to set the limits
|
||||
asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
|
||||
|
||||
friend class SeleneAxis;
|
||||
friend class pmacAxis;
|
||||
|
||||
protected:
|
||||
int setMotorPosition_;
|
||||
|
||||
};
|
||||
|
||||
#define EnableAxisString "ENABLE_AXIS"
|
||||
#define AxisEnabledString "AXIS_ENABLED"
|
||||
#define ReReadEncoderPositionString "REREAD_ENCODER_POSITION"
|
||||
#define ReReadEncoderPositionString_RBV "REREAD_ENCODER_POSITION_RBV"
|
||||
|
||||
class pmacV3Controller : public pmacController {
|
||||
public:
|
||||
pmacV3Controller(const char *portName, const char *lowLevelPortName,
|
||||
int lowLevelPortAddress, int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod, const int &extraParams = 4);
|
||||
|
||||
// overloaded because we want to enable/disable the motor
|
||||
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
|
||||
|
||||
// overloaded because we want to read the axis state
|
||||
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
|
||||
|
||||
friend class pmacV3Axis;
|
||||
friend class pmacAxis;
|
||||
friend class GirderAxis;
|
||||
|
||||
protected:
|
||||
pmacV3Axis **pAxes_; /**< Array of pointers to axis objects */
|
||||
|
||||
int enableAxis_;
|
||||
int axisEnabled_;
|
||||
int rereadEncoderPosition_;
|
||||
int rereadEncoderPositionRBV_;
|
||||
};
|
||||
|
||||
#endif /* pmacController_H */
|
||||
|
@ -3,8 +3,12 @@
|
||||
#---------------------------------------------
|
||||
registrar(EL734Register)
|
||||
registrar(PhytronRegister)
|
||||
registrar(EuroMoveRegister)
|
||||
registrar(NanotecRegister)
|
||||
registrar(pmacControllerRegister)
|
||||
registrar(pmacAsynIPPortRegister)
|
||||
registrar(MasterMACSRegister)
|
||||
registrar(SINQControllerRegister)
|
||||
|
||||
#--------------------------------------------------------
|
||||
# With the PSI module build system, including these items actually
|
||||
@ -16,6 +20,7 @@ registrar(pmacControllerRegister)
|
||||
#include "/ioc/modules/scaler/koennecke/R3.14.12/dbd/scaler.dbd"
|
||||
|
||||
device(scaler,INST_IO,devScalerEL737,"asynScalerEL737")
|
||||
device(stringin,INST_IO,devCounterBoxStringError,"devCounterBoxStringError")
|
||||
|
||||
#--------- For lakeshore and magnets
|
||||
#include "/ioc/modules/stream/2.7.9/R3.14.12/dbd/stream.dbd"
|
||||
|
88
testEuroMoveUsb.py
Executable file
88
testEuroMoveUsb.py
Executable file
@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
"""
|
||||
Created on Thur Feb 06 10:22:42 2020
|
||||
|
||||
@author: gaston emmanuel exil
|
||||
"""
|
||||
|
||||
#pip install pyusb
|
||||
#sudo cp 10-llbDevices.rules /etc/udev/rules.d/
|
||||
#sudo udevadm control --reload-rules && udevadm trigger
|
||||
#reboot
|
||||
|
||||
import sys
|
||||
import usb.core
|
||||
import usb.util
|
||||
from time import sleep
|
||||
|
||||
# 1. Device
|
||||
llbVendorID=0x04B4
|
||||
llbProductID=0x1002
|
||||
|
||||
# 2. Configuration
|
||||
CONFIGURATION_EV3 = 1 # 1-based
|
||||
# 3. Interface
|
||||
INTERFACE_EV3 = 0 # 0-based
|
||||
# 4. Alternate setting
|
||||
SETTING_EV3 = 0 # 0-based
|
||||
# 5. Endpoint
|
||||
ENDPOINT_EV3 = 1 # 0-based
|
||||
|
||||
# find our device
|
||||
device = usb.core.find(idVendor=llbVendorID, idProduct=llbProductID)
|
||||
|
||||
# was it found?
|
||||
if device is None:
|
||||
raise ValueError('Device not found')
|
||||
sys.exit(1)
|
||||
else:
|
||||
if device._manufacturer is None:
|
||||
device._manufacturer = usb.util.get_string(device, device.iManufacturer)
|
||||
print("manufacturer: ", str(device._manufacturer))
|
||||
if device._product is None:
|
||||
device._product = usb.util.get_string(device, device.iProduct)
|
||||
print("product: ", str(device._product))
|
||||
|
||||
|
||||
# By default, the kernel will claim the device and make it available via
|
||||
# /dev/usb/hiddevN and /dev/hidrawN which also prevents us
|
||||
# from communicating otherwise. This removes these kernel devices.
|
||||
# Yes, it is weird to specify an interface before we get to a configuration.
|
||||
|
||||
if device.is_kernel_driver_active(INTERFACE_EV3):
|
||||
print("Detaching kernel driver")
|
||||
device.detach_kernel_driver(INTERFACE_EV3)
|
||||
|
||||
# claim the device
|
||||
usb.util.claim_interface(device, INTERFACE_EV3)
|
||||
|
||||
# write endpoint
|
||||
endpoint_BulkOut = device[0][(0,0)][0]
|
||||
# Read endpoint
|
||||
endpoint_BulkIn = device[0][(0,0)][2]
|
||||
try:
|
||||
command = "L"+chr(13)
|
||||
assert device.write(endpoint_BulkOut, command.encode('utf-8'), 100) == len(command)
|
||||
ret = device.read(endpoint_BulkIn, endpoint_BulkIn.wMaxPacketSize, timeout=30000)
|
||||
print(ret)
|
||||
#byte_str = "".join(chr(n) for n in ret)
|
||||
print(ret.tostring())
|
||||
|
||||
|
||||
|
||||
command = "A1,4"+chr(13)
|
||||
assert device.write(endpoint_BulkOut, command.encode('utf-8'), 100) == len(command)
|
||||
ret = device.read(endpoint_BulkIn, endpoint_BulkIn.wMaxPacketSize, timeout=30000)
|
||||
print(ret)
|
||||
byte_str = "".join(chr(n) for n in ret)
|
||||
print(ret.tostring())
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
# release the device
|
||||
usb.util.release_interface(device, INTERFACE_EV3)
|
||||
# reattach the device to the OS kernel
|
||||
#device.attach_kernel_driver(INTERFACE_EV3)
|
23
testel734/db/basic_asyn_motor.db
Normal file
23
testel734/db/basic_asyn_motor.db
Normal file
@ -0,0 +1,23 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
field(RTRY,"0")
|
||||
}
|
||||
|
21
testel734/db/basic_motor.db
Normal file
21
testel734/db/basic_motor.db
Normal file
@ -0,0 +1,21 @@
|
||||
grecord(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"#C$(C) S$(S) @")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
}
|
||||
|
18
testel734/db/motorMessage.db
Normal file
18
testel734/db/motorMessage.db
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
record(ao,"$(P)m$(N)-Resolution"){
|
||||
field(DESC,"m$(N) Resolution")
|
||||
field(DOL,"$(P)m$(N).MRES CP MS")
|
||||
field(OMSL,"closed_loop")
|
||||
field(DTYP,"asynFloat64")
|
||||
field(OUT,"@asyn($(PORT),$(N))MOTOR_RESOLUTION")
|
||||
field(PREC,"3")
|
||||
}
|
48
testel734/db/sinq_asyn_motor.db
Normal file
48
testel734/db/sinq_asyn_motor.db
Normal file
@ -0,0 +1,48 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VMAX,"$(VMAX=$(VELO))")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
}
|
||||
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longin, "$(P)$(M):Enable_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) AXIS_ENABLED")
|
||||
field(PINI, "YES")
|
||||
field(SCAN, "1 second")
|
||||
}
|
||||
|
13
testel734/mota.sub
Normal file
13
testel734/mota.sub
Normal file
@ -0,0 +1,13 @@
|
||||
file "$(TOP)/db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DLLM, DHLM, INIT}
|
||||
{KM36:mota:, 2, "m$(N)", "asynMotor", mota, 2, "sgl", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, -20.0, 20.0, ""}
|
||||
}
|
||||
|
||||
file "$(TOP)/db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{KM36:mota:, 2, "m$(N)",mota}
|
||||
}
|
12
testel734/motaspeed.sub
Normal file
12
testel734/motaspeed.sub
Normal file
@ -0,0 +1,12 @@
|
||||
file "$(TOP)/db/sinq_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DLLM, DHLM, INIT, RDBD}
|
||||
{KM36:mota:,2,"m$(N)","asynMotor", mota, 2, "sgl", degree, Pos, 100, 0.1, .2, 0, 1, .2, 1., 3,-180.0,360.0,"", 0.2}}
|
||||
|
||||
file "$(TOP)/db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{KM36:mota:, 2, "m$(N)",mota}
|
||||
}
|
12
testel734/testel734.cmd
Executable file
12
testel734/testel734.cmd
Executable file
@ -0,0 +1,12 @@
|
||||
#!/usr/local/bin/iocsh
|
||||
|
||||
require sinq,koennecke
|
||||
|
||||
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp/testel734")
|
||||
|
||||
drvAsynIPPortConfigure("serial1", "localhost:61000",0,0,0)
|
||||
EL734CreateController("mota","serial1",3)
|
||||
|
||||
#dbLoadTemplate "mota.sub"
|
||||
dbLoadTemplate "motaspeed.sub"
|
||||
|
11
testel737.cmd
Executable file
11
testel737.cmd
Executable file
@ -0,0 +1,11 @@
|
||||
#!/usr/local/bin/iocsh
|
||||
|
||||
require sinq,koennecke
|
||||
|
||||
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp")
|
||||
|
||||
#--------- load EL737 counter box
|
||||
drvAsynIPPortConfigure("cter1","localhost:62000",0,0,0)
|
||||
dbLoadRecords("$(TOP)/db/asynRecord.db","P=KM36:,R=cter1,PORT=cter1,ADDR=0,OMAX=80,IMAX=80")
|
||||
dbLoadRecords("${TOP}/db/el737Record.db","P=KM36:counter,PORT=cter1,DESC=SANSCounter")
|
||||
|
9
testhuber/db/asynRecord.db
Normal file
9
testhuber/db/asynRecord.db
Normal file
@ -0,0 +1,9 @@
|
||||
record(asyn,"$(P)$(R)")
|
||||
{
|
||||
field(DTYP,"asynRecordDevice")
|
||||
field(PORT,"$(PORT)")
|
||||
field(ADDR,"$(ADDR)")
|
||||
field(OMAX,"$(OMAX)")
|
||||
field(IMAX,"$(IMAX)")
|
||||
}
|
||||
|
23
testhuber/db/basic_asyn_motor.db
Normal file
23
testhuber/db/basic_asyn_motor.db
Normal file
@ -0,0 +1,23 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
field(RTRY,"0")
|
||||
}
|
||||
|
21
testhuber/db/basic_motor.db
Normal file
21
testhuber/db/basic_motor.db
Normal file
@ -0,0 +1,21 @@
|
||||
grecord(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"#C$(C) S$(S) @")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
}
|
||||
|
18
testhuber/db/motorMessage.db
Normal file
18
testhuber/db/motorMessage.db
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
record(ao,"$(P)m$(N)-Resolution"){
|
||||
field(DESC,"m$(N) Resolution")
|
||||
field(DOL,"$(P)m$(N).MRES CP MS")
|
||||
field(OMSL,"closed_loop")
|
||||
field(DTYP,"asynFloat64")
|
||||
field(OUT,"@asyn($(PORT),$(N))MOTOR_RESOLUTION")
|
||||
field(PREC,"3")
|
||||
}
|
14
testhuber/db/pmacV3.db
Normal file
14
testhuber/db/pmacV3.db
Normal file
@ -0,0 +1,14 @@
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longin, "$(P)$(M):Enable_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) AXIS_ENABLED")
|
||||
field(PINI, "YES")
|
||||
field(SCAN, "1 second")
|
||||
}
|
47
testhuber/db/sinq_asyn_motor.db
Normal file
47
testhuber/db/sinq_asyn_motor.db
Normal file
@ -0,0 +1,47 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
}
|
||||
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longin, "$(P)$(M):Enable_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) AXIS_ENABLED")
|
||||
field(PINI, "YES")
|
||||
field(SCAN, "5 second")
|
||||
}
|
||||
|
33
testhuber/huber.cmd
Executable file
33
testhuber/huber.cmd
Executable file
@ -0,0 +1,33 @@
|
||||
#!/ioc/tools/iocsh
|
||||
|
||||
require motorHuber,brambilla_m
|
||||
|
||||
# epicsEnvSet("TOP","/ioc/sinq-ioc/boa-ioc")
|
||||
|
||||
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp/testhuber")
|
||||
|
||||
epicsEnvSet("STREAM_PROTOCOL_PATH","./db")
|
||||
epicsEnvSet("MOTOR",".")
|
||||
|
||||
# require autosave, koennecke
|
||||
|
||||
# Configure IP Port
|
||||
drvAsynIPPortConfigure("Huber1", "129.129.195.128:1234",0,0,0)
|
||||
|
||||
# Configure Controller
|
||||
asynOctetSetInputEos( "Huber1", -1, "\r")
|
||||
asynOctetSetOutputEos("Huber1", -1, "\r")
|
||||
|
||||
SMC9300CreateController("SMC1", "Huber1", 5, 50, 2000)
|
||||
|
||||
dbLoadTemplate "motor.substitutions.smc9300"
|
||||
|
||||
dbLoadRecords("$(TOP)/db/asynRecord.db","P=SQ:BOA:optics:,R=mcu,PORT=Huber1,ADDR=0,OMAX=80,IMAX=80")
|
||||
|
||||
|
||||
#--------------------------------- restore autosave
|
||||
#set_requestfile_path("$(TOP)", "")
|
||||
#set_savefile_path("$(TOP)/autosave")
|
||||
#save_restoreSet_Debug(0)
|
||||
#set_pass0_restoreFile("$(TOP)/autosave/huber.sav","")
|
||||
#set_pass1_restoreFile("$(TOP)/autosave/huber.sav","")
|
19
testhuber/motor.substitutions.smc9300
Normal file
19
testhuber/motor.substitutions.smc9300
Normal file
@ -0,0 +1,19 @@
|
||||
file "$(MOTOR)/db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR,DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, RDBD, INIT}
|
||||
{SQ:BOA:optics:, 1, "trans1","asynMotor", SMC1, 1, "translation 1",mm, Pos, 200, 10, 20, 0, 1, .2, 0.0001, 4, 150, -150, 0.1, ""}
|
||||
{SQ:BOA:optics:, 2, "lift1", "asynMotor", SMC1, 2, "lift 1", mm, Pos, 200, 10, 80, 0, 1, .2, 0.0001, 4, 100, 0, 0.1, ""}
|
||||
{SQ:BOA:optics:, 3, "trans2","asynMotor", SMC1, 3, "translation 2",mm, Pos, 200, 10, 80, 0, 1, .2, 0.0001, 4, 150, -150, 0.1, ""}
|
||||
{SQ:BOA:optics:, 4, "lift2", "asynMotor", SMC1, 4, "lift 2" ,mm, Pos, 200, 10, 80, 0, 1, .2, 0.0001, 4, 100, 0, 0.1, ""}
|
||||
}
|
||||
|
||||
file "$(TOP)/db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{SQ:BOA:optics:, 1, "trans1", SMC1}
|
||||
{SQ:BOA:optics:, 2, "lift1", SMC1}
|
||||
{SQ:BOA:optics:, 3, "trans2", SMC1}
|
||||
{SQ:BOA:optics:, 4, "lift2", SMC1}
|
||||
}
|
9
testmmac/db/asynRecord.db
Normal file
9
testmmac/db/asynRecord.db
Normal file
@ -0,0 +1,9 @@
|
||||
record(asyn,"$(P)$(R)")
|
||||
{
|
||||
field(DTYP,"asynRecordDevice")
|
||||
field(PORT,"$(PORT)")
|
||||
field(ADDR,"$(ADDR)")
|
||||
field(OMAX,"$(OMAX)")
|
||||
field(IMAX,"$(IMAX)")
|
||||
}
|
||||
|
23
testmmac/db/basic_asyn_motor.db
Normal file
23
testmmac/db/basic_asyn_motor.db
Normal file
@ -0,0 +1,23 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
field(RTRY,"0")
|
||||
}
|
||||
|
21
testmmac/db/basic_motor.db
Normal file
21
testmmac/db/basic_motor.db
Normal file
@ -0,0 +1,21 @@
|
||||
grecord(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"#C$(C) S$(S) @")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
}
|
||||
|
18
testmmac/db/motorMessage.db
Normal file
18
testmmac/db/motorMessage.db
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
record(ao,"$(P)m$(N)-Resolution"){
|
||||
field(DESC,"m$(N) Resolution")
|
||||
field(DOL,"$(P)m$(N).MRES CP MS")
|
||||
field(OMSL,"closed_loop")
|
||||
field(DTYP,"asynFloat64")
|
||||
field(OUT,"@asyn($(PORT),$(N))MOTOR_RESOLUTION")
|
||||
field(PREC,"3")
|
||||
}
|
14
testmmac/db/pmacV3.db
Normal file
14
testmmac/db/pmacV3.db
Normal file
@ -0,0 +1,14 @@
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longin, "$(P)$(M):Enable_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) AXIS_ENABLED")
|
||||
field(PINI, "YES")
|
||||
field(SCAN, "1 second")
|
||||
}
|
47
testmmac/db/sinq_asyn_motor.db
Normal file
47
testmmac/db/sinq_asyn_motor.db
Normal file
@ -0,0 +1,47 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
}
|
||||
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longin, "$(P)$(M):Enable_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) AXIS_ENABLED")
|
||||
field(PINI, "YES")
|
||||
field(SCAN, "5 second")
|
||||
}
|
||||
|
15
testmmac/mmacs.sub
Normal file
15
testmmac/mmacs.sub
Normal file
@ -0,0 +1,15 @@
|
||||
file "$(TOP)/db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DLLM, DHLM, INIT}
|
||||
{KM36:mota:, 5, "m$(N)", "asynMotor", mota, 5, "m5", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, -20.0, 20.0, ""}
|
||||
{KM36:mota:, 6, "m$(N)", "asynMotor", mota, 6, "m6", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, -20.0, 20.0, ""}
|
||||
}
|
||||
|
||||
file "$(TOP)/db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{KM36:mota:, 5, "m$(N)",mota}
|
||||
{KM36:mota:, 6, "m$(N)",mota}
|
||||
}
|
8
testmmac/mmacs2.sub
Normal file
8
testmmac/mmacs2.sub
Normal file
@ -0,0 +1,8 @@
|
||||
file "$(TOP)/db/sinq_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DLLM, DHLM, RDBD, INIT}
|
||||
{KM36:mota:,5,"m$(N)","asynMotor", mota, 5, "m5", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3,-400000.000,400000.000,.01, ""}
|
||||
{KM36:mota:,6,"m$(N)","asynMotor", mota, 6, "m6", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3,-200000.000,200000.000,.01, ""}
|
||||
}
|
||||
|
13
testmmac/mota.sub
Normal file
13
testmmac/mota.sub
Normal file
@ -0,0 +1,13 @@
|
||||
file "$(TOP)/db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DLLM, DHLM, INIT}
|
||||
{KM36:mota:, 2, "m$(N)", "asynMotor", mota, 2, "sgl", degree, Pos, 2.0, 0.1, .2, 0, 1, .2, 1., 3, -20.0, 20.0, ""}
|
||||
}
|
||||
|
||||
file "$(TOP)/db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{KM36:mota:, 2, "m$(N)",mota}
|
||||
}
|
12
testmmac/motaspeed.sub
Normal file
12
testmmac/motaspeed.sub
Normal file
@ -0,0 +1,12 @@
|
||||
file "$(TOP)/db/sinq_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DLLM, DHLM, INIT, RDBD}
|
||||
{KM36:mota:,2,"m$(N)","asynMotor", mota, 2, "sgl", degree, Pos, 100, 0.1, .2, 0, 1, .2, 1., 3,-180.0,360.0,"", 0.2}}
|
||||
|
||||
file "$(TOP)/db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{KM36:mota:, 2, "m$(N)",mota}
|
||||
}
|
35
testmmac/testmmacs.cmd
Executable file
35
testmmac/testmmacs.cmd
Executable file
@ -0,0 +1,35 @@
|
||||
#!/usr/local/bin/iocsh
|
||||
|
||||
require sinq,koennecke
|
||||
require autosave,koennecke
|
||||
|
||||
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp/testmmac")
|
||||
|
||||
set_requestfile_path("$(TOP)", "")
|
||||
set_savefile_path("$(TOP)/autosave")
|
||||
save_restoreSet_Debug(0)
|
||||
#--------------------------------- restore autosave
|
||||
set_pass0_restoreFile("$(TOP)/autosave/test.sav","")
|
||||
set_pass1_restoreFile("$(TOP)/autosave/test.sav","")
|
||||
|
||||
|
||||
#drvAsynMMACSPortConfigure("macs1", "localhost:8080",0,0,0)
|
||||
#asynInterposeEosConfig("macs1", 0, 0, 0)
|
||||
#dbLoadRecords("$(TOP)/db/asynRecord.db","P=KM36:,R=macs1,PORT=macs1,ADDR=0,OMAX=80,IMAX=80")
|
||||
#asynSetTraceMask("macs1", 0, 255)
|
||||
|
||||
#asynSetTraceMask("macs1", 0, 255)
|
||||
#drvAsynIPPortConfigure("macs1", "localhost:8080",0,0,0)
|
||||
drvAsynIPPortConfigure("macs1", "Marcel1--MACS:1917")
|
||||
MasterMACSCreateController("mota","macs1",7, 500, 10000)
|
||||
MasterMACSCreateAxis("mota",5)
|
||||
MasterMACSCreateAxis("mota",6)
|
||||
|
||||
dbLoadTemplate "mmacs2.sub"
|
||||
|
||||
iocInit()
|
||||
setMovingPollPeriod("mota", .5)
|
||||
setIdlePollPeriod("mota", 10. )
|
||||
|
||||
create_monitor_set("test.req", 10, "")
|
||||
|
9
testpmacV3/db/asynRecord.db
Normal file
9
testpmacV3/db/asynRecord.db
Normal file
@ -0,0 +1,9 @@
|
||||
record(asyn,"$(P)$(R)")
|
||||
{
|
||||
field(DTYP,"asynRecordDevice")
|
||||
field(PORT,"$(PORT)")
|
||||
field(ADDR,"$(ADDR)")
|
||||
field(OMAX,"$(OMAX)")
|
||||
field(IMAX,"$(IMAX)")
|
||||
}
|
||||
|
22
testpmacV3/db/basic_asyn_motor.db
Normal file
22
testpmacV3/db/basic_asyn_motor.db
Normal file
@ -0,0 +1,22 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(DHLM,"$(DHLM)")
|
||||
field(DLLM,"$(DLLM)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(TWV,"1")
|
||||
field(RTRY,"0")
|
||||
}
|
||||
|
19544
testpmacV3/db/huber.dbd
Normal file
19544
testpmacV3/db/huber.dbd
Normal file
File diff suppressed because it is too large
Load Diff
9
testpmacV3/db/motorMessage.db
Normal file
9
testpmacV3/db/motorMessage.db
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
14
testpmacV3/db/pmacV3.db
Normal file
14
testpmacV3/db/pmacV3.db
Normal file
@ -0,0 +1,14 @@
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longin, "$(P)$(M):Enable_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) AXIS_ENABLED")
|
||||
field(PINI, "YES")
|
||||
field(SCAN, "1 second")
|
||||
}
|
27
testpmacV3/mcu.substitutions
Normal file
27
testpmacV3/mcu.substitutions
Normal file
@ -0,0 +1,27 @@
|
||||
file "$(TOP)/db/basic_asyn_motor.db"
|
||||
{
|
||||
pattern
|
||||
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}
|
||||
{SQ:TEST:mcu:, 2, "m$(N)", "asynMotor", mcu, 2, "m$(N)", degrees, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 358., 2., ""}
|
||||
{SQ:TEST:mcu:, 6, "m$(N)", "asynMotor", mcu, 6, "m$(N)", degrees, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 358., 2., ""}
|
||||
{SQ:TEST:mcu:,10, "m$(N)", "asynMotor", mcu,10, "m$(N)", degrees, Pos, 2.0, 0.1, .2, 0, 1, .2, .001, 3, 358., 2., ""}
|
||||
}
|
||||
|
||||
file "$(TOP)/db/motorMessage.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{SQ:TEST:mcu:, 2, "m$(N)", mcu}
|
||||
{SQ:TEST:mcu:, 6, "m$(N)", mcu}
|
||||
{SQ:TEST:mcu:,10, "m$(N)", mcu}
|
||||
}
|
||||
|
||||
file "$(TOP)/db/pmacV3.db"
|
||||
{
|
||||
pattern
|
||||
{P,N, M,PORT}
|
||||
{SQ:TEST:mcu:, 2, "m$(N)", mcu}
|
||||
{SQ:TEST:mcu:, 6, "m$(N)", mcu}
|
||||
{SQ:TEST:mcu:,10, "m$(N)", mcu}
|
||||
}
|
||||
|
25
testpmacV3/st.cmd
Executable file
25
testpmacV3/st.cmd
Executable file
@ -0,0 +1,25 @@
|
||||
#!/ioc/tools/iocsh
|
||||
|
||||
require sinq,brambilla_m
|
||||
#require sinq,koennecke
|
||||
|
||||
epicsEnvSet("TOP","/afs/psi.ch/project/sinqdev/sinqepicsapp/testpmacV3")
|
||||
|
||||
epicsEnvSet("EPICS_CA_ADDR_LIST","127.0.0.1")
|
||||
|
||||
epicsEnvSet("STREAM_PROTOCOL_PATH","$(TOP)/db")
|
||||
|
||||
|
||||
var streamDebug 3
|
||||
|
||||
# motors
|
||||
|
||||
pmacAsynIPConfigure("pmacV3", "129.129.138.234:1025")
|
||||
pmacV3CreateController("mcu","pmacV3",0,16,50,1000);
|
||||
pmacV3CreateAxis("mcu",2,0);
|
||||
pmacV3CreateAxis("mcu",6,0);
|
||||
pmacV3CreateAxis("mcu",10,0);
|
||||
dbLoadTemplate "$(TOP)/mcu.substitutions"
|
||||
dbLoadRecords("$(TOP)/db/asynRecord.db","P=SQ:TEST:,R=mcu,PORT=pmacV3,ADDR=0,OMAX=80,IMAX=80")
|
||||
|
||||
iocInit
|
56
utils/convertsub.py
Executable file
56
utils/convertsub.py
Executable file
@ -0,0 +1,56 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
This is a little program which generates soft motor initialisation lines and iocsh morsels
|
||||
from a motor substitutions file.
|
||||
|
||||
Mark Koennecke, December 2019
|
||||
"""
|
||||
import sys
|
||||
import pdb
|
||||
import os
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print('Usage:\n\tconvertsub XXXX.substitutions')
|
||||
sys.exit(1)
|
||||
|
||||
subLen = 0
|
||||
motors = []
|
||||
port = None
|
||||
|
||||
with open(sys.argv[1],'r') as fin:
|
||||
line = fin.readline()
|
||||
while line:
|
||||
line = line.replace(' ','')
|
||||
line = line.strip('{}')
|
||||
l = line.split(',')
|
||||
if line.find('DHLM') > 0: # that is the index discovery line
|
||||
lowlimidx = l.index('DLLM')
|
||||
highlimidx = l.index('DHLM')
|
||||
mresidx = l.index('MRES')
|
||||
addridx = l.index('ADDR')
|
||||
prefix = l[0]
|
||||
subLen = len(l)
|
||||
portIdx = l.index('PORT')
|
||||
elif subLen > 0:
|
||||
l = line.split(',')
|
||||
if subLen == len(l):
|
||||
motors.append((l[addridx],l[mresidx],l[lowlimidx],l[highlimidx]))
|
||||
port = l[portIdx]
|
||||
else:
|
||||
pass
|
||||
line = fin.readline()
|
||||
|
||||
print('%d motors found' % len(motors))
|
||||
|
||||
fname = os.path.splitext(sys.argv[1])[0] + '.cmd'
|
||||
|
||||
out = open(fname,'w')
|
||||
out.write('motorSimCreateController(\"%s\",%d)\n' % (port, len(motors)+2))
|
||||
for mot in motors:
|
||||
mres = float(mot[1])
|
||||
highlim = int(float(mot[3])/mres)
|
||||
lowlim = int(float(mot[2])/mres)
|
||||
out.write('motorSimConfigAxis(\"%s\",%s,%d,%d,0,0)\n' %(port,mot[0],highlim,lowlim))
|
||||
out.write('dbLoadTemplate(\"$(TOP)/%s\")\n' % (sys.argv[1]))
|
||||
out.close()
|
||||
print(fname + ' written')
|
177
utils/deltatau.py
Executable file
177
utils/deltatau.py
Executable file
@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
This is some code for communicating with a Delta-Tau PMAC motor
|
||||
controller as used at SINQ from python. Python 3 is assumed and
|
||||
tested. We use the ethernet protocol of the Pmac. This code benefits
|
||||
from the SICS driver for Pmac which in turn benefits from the driver
|
||||
written by Pete Leicester at Diamond.
|
||||
|
||||
|
||||
Usage: python36 deltatau.py pmachost:port command
|
||||
Then returns the response for command.
|
||||
|
||||
Mark Koennecke, October 2019
|
||||
"""
|
||||
|
||||
import struct
|
||||
import socket
|
||||
import curses
|
||||
|
||||
def packPmacCommand(command):
|
||||
# 0x40 = VR_DOWNLOAD
|
||||
# 0xBF = VR_PMAC_GETRESPONSE
|
||||
buf = struct.pack('BBHHH',0x40,0xBF,0,0,socket.htons(len(command)))
|
||||
buf = buf + bytes(command,'utf-8')
|
||||
return buf
|
||||
|
||||
def readPmacReply(input):
|
||||
msg = bytearray()
|
||||
expectAck = True
|
||||
while True:
|
||||
b = input.recv(1)
|
||||
bint = int.from_bytes(b,byteorder='little')
|
||||
if bint == 2 or bint == 7: #STX or BELL
|
||||
expectAck = False
|
||||
continue
|
||||
if expectAck and bint == 6: # ACK
|
||||
return bytes(msg)
|
||||
else:
|
||||
if bint == 13 and not expectAck: # CR
|
||||
return bytes(msg)
|
||||
else:
|
||||
msg.append(bint)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from sys import argv
|
||||
|
||||
try:
|
||||
|
||||
addr = argv[1].split(':')
|
||||
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
s.connect((addr[0],int(addr[1])))
|
||||
|
||||
if len(argv) == 3:
|
||||
buf = packPmacCommand(argv[2])
|
||||
s.send(buf)
|
||||
reply = readPmacReply(s)
|
||||
print(reply.decode('utf-8') + '\n')
|
||||
|
||||
else:
|
||||
|
||||
try:
|
||||
|
||||
stdscr = curses.initscr()
|
||||
curses.noecho()
|
||||
curses.cbreak()
|
||||
stdscr.keypad(True)
|
||||
|
||||
stdscr.addstr(">> ")
|
||||
stdscr.refresh()
|
||||
|
||||
history = [""]
|
||||
ptr = len(history) - 1
|
||||
|
||||
while True:
|
||||
c = stdscr.getch()
|
||||
if c == curses.KEY_RIGHT:
|
||||
(y, x) = stdscr.getyx()
|
||||
if x < len(history[ptr]) + 3:
|
||||
stdscr.move(y, x+1)
|
||||
stdscr.refresh()
|
||||
elif c == curses.KEY_LEFT:
|
||||
(y, x) = stdscr.getyx()
|
||||
if x > 3:
|
||||
stdscr.move(y, x-1)
|
||||
stdscr.refresh()
|
||||
elif c == curses.KEY_UP:
|
||||
if ptr > 0:
|
||||
ptr -= 1
|
||||
stdscr.addch("\r")
|
||||
stdscr.clrtoeol()
|
||||
stdscr.addstr(">> " + history[ptr])
|
||||
elif c == curses.KEY_DOWN:
|
||||
if ptr < len(history) - 1:
|
||||
ptr += 1
|
||||
stdscr.addch("\r")
|
||||
stdscr.clrtoeol()
|
||||
stdscr.addstr(">> " + history[ptr])
|
||||
elif c == curses.KEY_ENTER or c == ord('\n') or c == ord('\r'):
|
||||
if history[ptr] == 'quit':
|
||||
break
|
||||
|
||||
# because of arrow keys move back to the end of the line
|
||||
(y, x) = stdscr.getyx()
|
||||
stdscr.move(y, 3+len(history[ptr]))
|
||||
|
||||
if history[ptr]:
|
||||
buf = packPmacCommand(history[ptr])
|
||||
s.send(buf)
|
||||
reply = readPmacReply(s)
|
||||
stdscr.addstr("\n" + reply.decode('utf-8')[0:-1])
|
||||
|
||||
if ptr == len(history) - 1 and history[ptr] != "":
|
||||
history += [""]
|
||||
else:
|
||||
history[-1] = ""
|
||||
ptr = len(history) - 1
|
||||
|
||||
stdscr.addstr("\n>> ")
|
||||
stdscr.refresh()
|
||||
|
||||
else:
|
||||
if ptr < len(history) - 1: # Modifying previous input
|
||||
if len(history[-1]) == 0:
|
||||
history[-1] = history[ptr]
|
||||
ptr = len(history) - 1
|
||||
|
||||
else:
|
||||
history += [history[ptr]]
|
||||
ptr = len(history) - 1
|
||||
|
||||
if c == curses.KEY_BACKSPACE:
|
||||
if len(history[ptr]) == 0:
|
||||
continue
|
||||
(y, x) = stdscr.getyx()
|
||||
history[ptr] = history[ptr][0:x-4] + history[ptr][x-3:]
|
||||
stdscr.addch("\r")
|
||||
stdscr.clrtoeol()
|
||||
stdscr.addstr(">> " + history[ptr])
|
||||
stdscr.move(y, x-1)
|
||||
stdscr.refresh()
|
||||
|
||||
else:
|
||||
(y, x) = stdscr.getyx()
|
||||
history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:]
|
||||
stdscr.addch("\r")
|
||||
stdscr.clrtoeol()
|
||||
stdscr.addstr(">> " + history[ptr])
|
||||
stdscr.move(y, x+1)
|
||||
stdscr.refresh()
|
||||
|
||||
finally:
|
||||
|
||||
# to quit
|
||||
curses.nocbreak()
|
||||
stdscr.keypad(False)
|
||||
curses.echo()
|
||||
curses.endwin()
|
||||
|
||||
except:
|
||||
print("""
|
||||
Invalid Arguments
|
||||
|
||||
Option 1: Single Command
|
||||
------------------------
|
||||
|
||||
Usage: deltatau.py pmachost:port command
|
||||
This then returns the response for command.
|
||||
|
||||
Option 2: CLI Mode
|
||||
------------------
|
||||
|
||||
Usage: deltatau.py pmachost:port
|
||||
|
||||
You can then type in a command, hit enter, and the response will see
|
||||
the reponse, before being prompted to again enter a command. Type
|
||||
'quit' to close prompt.
|
||||
""")
|
54
utils/macmaster.py
Executable file
54
utils/macmaster.py
Executable file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Usage: macmaster.py macmasterhost port
|
||||
Listen for commands to send and returns reponse
|
||||
until exit has been typed
|
||||
|
||||
Mark Koennecke, March 2023
|
||||
"""
|
||||
|
||||
import socket
|
||||
import struct
|
||||
|
||||
|
||||
class MasterMACS():
|
||||
def __init__(self, host, port):
|
||||
self._socke = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._socke.connect((host, int(port)))
|
||||
|
||||
def send(self, command):
|
||||
buf = struct.pack('BBBB', 0x05, len(command) + 4, 0, 0x19)
|
||||
buf += bytes(command, 'utf-8')
|
||||
buf += struct.pack('BB', 0x0D, 0x03)
|
||||
self._socke.send(buf)
|
||||
|
||||
def receive(self):
|
||||
buf = self._socke.recv(35, socket.MSG_WAITALL)
|
||||
if len(buf) < 35:
|
||||
raise EOFException('Master MACS returned only %d bytes, 35 expected' % len(buf))
|
||||
idx = buf.find(0x0D)
|
||||
ackbyte = buf[idx-1]
|
||||
if ackbyte == 0x06:
|
||||
ack = 'ACK'
|
||||
elif ackbyte == 0x15:
|
||||
ack = 'NAK',
|
||||
else:
|
||||
ack = 'NO'
|
||||
reply = buf[4:idx-1].decode('utf-8')
|
||||
return ack, reply
|
||||
|
||||
if __name__ == "__main__":
|
||||
from sys import argv, exit, stdin
|
||||
if len(argv) < 3:
|
||||
print('Usage:\n\tmacmaster.py machost macport')
|
||||
exit(1)
|
||||
mac = MasterMACS(argv[1], argv[2])
|
||||
while(True):
|
||||
# import pdb; pdb.set_trace()
|
||||
line = stdin.readline()
|
||||
if line.find('exit') >= 0:
|
||||
exit(0)
|
||||
mac.send(line.strip())
|
||||
ack, reply = mac.receive()
|
||||
print('%s, %s' %(ack, reply))
|
||||
|
58
utils/physhell.tcl
Executable file
58
utils/physhell.tcl
Executable file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/tclsh
|
||||
# This is a little program which acts as a shell to the phytron.
|
||||
# It connects to another program which is supposed to talk to the phytron controller
|
||||
# It reads from stdin, packages the message into the phytron format and sends it to
|
||||
# the phytron communication program. Then it reads from the phytron communication program
|
||||
# and unpacks the reply.
|
||||
#
|
||||
# This is also a nice exampe for dealing with binary in Tcl
|
||||
# Making the binary character only worked with %c
|
||||
# The only way to do the comparison is with the string comare
|
||||
#
|
||||
# Mark Könnecke, September 2016
|
||||
|
||||
|
||||
if {[llength $argv] < 1} {
|
||||
puts stdout "Usage:\n\t physhell.tcl phytronprogram"
|
||||
exit
|
||||
}
|
||||
|
||||
set phprogram [lindex $argv 0]
|
||||
|
||||
set phyio [open "| $phprogram" "w+b"]
|
||||
fconfigure $phyio -buffering none
|
||||
fconfigure $phyio -translation {binary binary}
|
||||
set etx [format "%c" 0x03]
|
||||
set stx [format "%c" 0x02]
|
||||
set ack [format "%c" 0x06]
|
||||
set nack [format "%c" 0x05]
|
||||
|
||||
|
||||
while {1} {
|
||||
set inp [gets stdin]
|
||||
puts -nonewline $phyio [format "%c%s%c" 0x02 $inp 0x03]
|
||||
set mode start
|
||||
set reply ""
|
||||
while {[string compare $mode done] != 0 } {
|
||||
set c [read $phyio 1]
|
||||
switch $mode {
|
||||
start {
|
||||
if {[string compare $c $stx] == 0} {
|
||||
set mode data
|
||||
}
|
||||
}
|
||||
data {
|
||||
if {[string compare $c $etx] == 0} {
|
||||
puts stdout $reply
|
||||
set mode done
|
||||
} elseif {[string compare $c $nack] == 0} {
|
||||
append reply NACK
|
||||
} elseif {[string compare $c $ack] == 0} {
|
||||
append reply ACK
|
||||
} else {
|
||||
append reply $c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
114
utils/syncEL734Sub.py
Executable file
114
utils/syncEL734Sub.py
Executable file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
This little program synchronizes an EPICS substitutions file with values read
|
||||
from the EL734 motor controller. Some research showed that the EPICS motorRecord
|
||||
seems to have no means of reading configuration data like limits or speeds or such
|
||||
from the device support. Than it is the easiest to read the data with this small
|
||||
program and update the substitutions file with it.
|
||||
|
||||
Mark Koennecke, January 2023
|
||||
"""
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
socke = None
|
||||
sockfd = None
|
||||
|
||||
# --------------- communication section
|
||||
def connect(host, port):
|
||||
global socke, sockfd
|
||||
socke = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
# socke.connect(('localhost', 5050))
|
||||
socke.connect((host, port))
|
||||
socke.settimeout(2.)
|
||||
sockfd = socke.makefile(mode='rw', newline='\r')
|
||||
sockfd.write('RMT 1\r')
|
||||
sockfd.flush()
|
||||
sockfd.write('ECHO 0\r')
|
||||
sockfd.flush()
|
||||
sockfd.readline()
|
||||
|
||||
def transact(command):
|
||||
global socke, sockfd
|
||||
sockfd.write(command + '\r')
|
||||
sockfd.flush()
|
||||
time.sleep(.1)
|
||||
return sockfd.readline()
|
||||
|
||||
def find_commas(rawline):
|
||||
comma_list = []
|
||||
for i in range(len(rawline)):
|
||||
if rawline[i] == ',':
|
||||
comma_list.append(i)
|
||||
return comma_list
|
||||
|
||||
def pretty_line(newlist, comma_list):
|
||||
"""
|
||||
adds spaces to match the tabbing of the pattern line
|
||||
"""
|
||||
newline = ''
|
||||
for item, idx in zip(newlist, comma_list):
|
||||
length = len(newline) + 1 + len(item)
|
||||
if length < idx:
|
||||
newline += ' ' * (idx - length)
|
||||
newline += item
|
||||
newline += ','
|
||||
newline += newlist[-1]
|
||||
return newline[0:-1]
|
||||
|
||||
def fix_line(par_list, index_list):
|
||||
# import pdb; pdb.set_trace()
|
||||
addridx = index_list.index('ADDR')
|
||||
motNo = par_list[addridx]
|
||||
limits = transact('H ' + motNo)
|
||||
time.sleep(.1)
|
||||
l = limits.split()
|
||||
lowidx = index_list.index('DLLM')
|
||||
# import pdb; pdb.set_trace()
|
||||
par_list[lowidx] = l[0]
|
||||
highidx = index_list.index('DHLM')
|
||||
par_list[highidx] = l[1]
|
||||
speed = transact('J ' + motNo)
|
||||
vidx = index_list.index('VELO')
|
||||
par_list[vidx] = speed.strip()
|
||||
# vidx = index_list.index('VMAX')
|
||||
# par_list[vidx] = speed.strip()
|
||||
return par_list
|
||||
|
||||
def scan_substitution_file(filename):
|
||||
index_list = None
|
||||
# import pdb; pdb.set_trace()
|
||||
with open(filename, 'r') as fin:
|
||||
rawline = fin.readline()
|
||||
while rawline:
|
||||
line = rawline.replace(' ','')
|
||||
line = line.strip('{}')
|
||||
l = line.split(',')
|
||||
if line.find('DHLM') > 0:
|
||||
index_list = l
|
||||
comma_list = find_commas(rawline)
|
||||
sys.stdout.write(rawline)
|
||||
elif not index_list:
|
||||
sys.stdout.write(rawline)
|
||||
else:
|
||||
if len(l) == len(index_list):
|
||||
newlist = fix_line(l, index_list)
|
||||
# newline = ','.join(newlist)
|
||||
newline = pretty_line(newlist, comma_list)
|
||||
sys.stdout.write('{' + newline + '\n')
|
||||
else:
|
||||
sys.stdout.write(rawline)
|
||||
rawline = fin.readline()
|
||||
|
||||
#------------------ main
|
||||
if len(sys.argv) < 4:
|
||||
print('Usage:\n\tsyncEL734sub.py <host> <port> <substitutions-file>\n')
|
||||
sys.exit(1)
|
||||
|
||||
connect(sys.argv[1], int(sys.argv[2]))
|
||||
scan_substitution_file(sys.argv[3])
|
||||
socke.close()
|
||||
|
||||
|
98
utils/syncMasterMAC.py
Executable file
98
utils/syncMasterMAC.py
Executable file
@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
This little program synchronizes an EPICS substitutions file with values read
|
||||
from a MasterMAC motor controller.
|
||||
|
||||
Mark Koennecke, February 2023
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
|
||||
from macmaster import MasterMACS
|
||||
|
||||
def find_commas(rawline):
|
||||
comma_list = []
|
||||
for i in range(len(rawline)):
|
||||
if rawline[i] == ',':
|
||||
comma_list.append(i)
|
||||
return comma_list
|
||||
|
||||
def pretty_line(newlist, comma_list):
|
||||
"""
|
||||
adds spaces to match the tabbing of the patter line
|
||||
"""
|
||||
newline = ''
|
||||
for item, idx in zip(newlist, comma_list):
|
||||
length = len(newline) + 1 + len(item)
|
||||
if length < idx:
|
||||
newline += ' ' * (idx - length)
|
||||
newline += item
|
||||
newline += ','
|
||||
newline += newlist[-1]
|
||||
return newline[0:-1]
|
||||
|
||||
def transact(command):
|
||||
global mac
|
||||
mac.send(command)
|
||||
time.sleep(.1)
|
||||
return mac.receive()
|
||||
|
||||
def fix_line(par_list, index_list):
|
||||
# import pdb; pdb.set_trace()
|
||||
addridx = index_list.index('ADDR')
|
||||
motNo = int(par_list[addridx])
|
||||
ack, reply = transact('%dR24' % motNo)
|
||||
if ack == 'NO':
|
||||
idx = reply.find('=')
|
||||
lowlim = reply[idx+1:]
|
||||
lowidx = index_list.index('DLLM')
|
||||
par_list[lowidx] = lowlim.strip()
|
||||
ack, reply = transact('%dR23' % motNo)
|
||||
if ack == 'NO':
|
||||
idx = reply.find('=')
|
||||
highlim = reply[idx+1:]
|
||||
highidx = index_list.index('DHLM')
|
||||
par_list[highidx] = highlim.strip()
|
||||
ack, reply = transact('%dR05' % motNo)
|
||||
if ack == 'NO':
|
||||
idx = reply.find('=')
|
||||
speed = reply[idx+1:]
|
||||
speedidx = index_list.index('VELO')
|
||||
par_list[speedidx] = speed.strip()
|
||||
return par_list
|
||||
|
||||
def scan_substitution_file(filename):
|
||||
index_list = None
|
||||
# import pdb; pdb.set_trace()
|
||||
with open(filename, 'r') as fin:
|
||||
rawline = fin.readline()
|
||||
# import pdb; pdb.set_trace()
|
||||
while rawline:
|
||||
line = rawline.replace(' ','')
|
||||
line = line.strip('{}')
|
||||
l = line.split(',')
|
||||
if line.find('DHLM') > 0:
|
||||
index_list = l
|
||||
comma_list = find_commas(rawline)
|
||||
sys.stdout.write(rawline)
|
||||
elif not index_list:
|
||||
sys.stdout.write(rawline)
|
||||
else:
|
||||
if len(l) == len(index_list):
|
||||
newlist = fix_line(l, index_list)
|
||||
# newline = ','.join(newlist)
|
||||
newline = pretty_line(newlist, comma_list)
|
||||
sys.stdout.write('{' + newline + '\n')
|
||||
else:
|
||||
sys.stdout.write(rawline)
|
||||
rawline = fin.readline()
|
||||
|
||||
#------------------ main
|
||||
if len(sys.argv) < 4:
|
||||
print('Usage:\n\tsyncMMACSub.py <host> <port> <substitutions-file>\n')
|
||||
sys.exit(1)
|
||||
|
||||
mac = MasterMACS(sys.argv[1], sys.argv[2])
|
||||
scan_substitution_file(sys.argv[3])
|
||||
|
||||
|
104
utils/syncPmacSub.py
Executable file
104
utils/syncPmacSub.py
Executable file
@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
This little program synchronizes an EPICS substitutions file with values read
|
||||
from the delta tau motor controller. Some research showed that the EPICS motorRecord
|
||||
seems to have no means of reading configuration data like limits or speeds or such
|
||||
from the device support. Than it is the easiest to read the data with this small
|
||||
program and update the substitutions file with it.
|
||||
|
||||
Mark Koennecke, January 2023
|
||||
"""
|
||||
|
||||
import socket
|
||||
import sys
|
||||
from deltatau import packPmacCommand, readPmacReply
|
||||
|
||||
|
||||
socke = None
|
||||
|
||||
# --------------- communication section
|
||||
def connect(host, port):
|
||||
global socke, sockfd
|
||||
socke = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
# socke.connect(('localhost', 5050))
|
||||
socke.connect((host, port))
|
||||
socke.settimeout(1.)
|
||||
|
||||
def transact(command):
|
||||
global socke
|
||||
data = packPmacCommand(command)
|
||||
socke.send(data)
|
||||
reply = readPmacReply(socke)
|
||||
return reply.decode('utf-8')
|
||||
|
||||
def find_commas(rawline):
|
||||
comma_list = []
|
||||
for i in range(len(rawline)):
|
||||
if rawline[i] == ',':
|
||||
comma_list.append(i)
|
||||
return comma_list
|
||||
|
||||
def pretty_line(newlist, comma_list):
|
||||
"""
|
||||
adds spaces to match the tabbing of the patter line
|
||||
"""
|
||||
newline = ''
|
||||
for item, idx in zip(newlist, comma_list):
|
||||
length = len(newline) + 1 + len(item)
|
||||
if length < idx:
|
||||
newline += ' ' * (idx - length)
|
||||
newline += item
|
||||
newline += ','
|
||||
newline += newlist[-1]
|
||||
return newline[0:-1]
|
||||
|
||||
def fix_line(par_list, index_list):
|
||||
# import pdb; pdb.set_trace()
|
||||
addridx = index_list.index('ADDR')
|
||||
motNo = int(par_list[addridx])
|
||||
lowlim = transact('Q%d14' % motNo)
|
||||
lowidx = index_list.index('DLLM')
|
||||
par_list[lowidx] = lowlim.strip()
|
||||
highlim = transact('Q%d13' % motNo)
|
||||
highidx = index_list.index('DHLM')
|
||||
par_list[highidx] = highlim.strip()
|
||||
speed = transact('Q%d03' % motNo)
|
||||
speedidx = index_list.index('VELO')
|
||||
par_list[speedidx] = speed.strip()
|
||||
return par_list
|
||||
|
||||
def scan_substitution_file(filename):
|
||||
index_list = None
|
||||
# import pdb; pdb.set_trace()
|
||||
with open(filename, 'r') as fin:
|
||||
rawline = fin.readline()
|
||||
while rawline:
|
||||
line = rawline.replace(' ','')
|
||||
line = line.strip('{}')
|
||||
l = line.split(',')
|
||||
if line.find('DHLM') > 0:
|
||||
index_list = l
|
||||
comma_list = find_commas(rawline)
|
||||
sys.stdout.write(rawline)
|
||||
elif not index_list:
|
||||
sys.stdout.write(rawline)
|
||||
else:
|
||||
if len(l) == len(index_list):
|
||||
newlist = fix_line(l, index_list)
|
||||
# newline = ','.join(newlist)
|
||||
newline = pretty_line(newlist, comma_list)
|
||||
sys.stdout.write('{' + newline + '\n')
|
||||
else:
|
||||
sys.stdout.write(rawline)
|
||||
rawline = fin.readline()
|
||||
|
||||
#------------------ main
|
||||
if len(sys.argv) < 4:
|
||||
print('Usage:\n\tsyncPmacSub.py <host> <port> <substitutions-file>\n')
|
||||
sys.exit(1)
|
||||
|
||||
connect(sys.argv[1], int(sys.argv[2]))
|
||||
scan_substitution_file(sys.argv[3])
|
||||
socke.close()
|
||||
|
||||
|
Reference in New Issue
Block a user