Compare commits
39 Commits
pmacV3
...
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 |
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
|
||||
...
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,6 +5,9 @@
|
||||
# Ignore text editor (e.g. emacs) autosave files
|
||||
*~
|
||||
|
||||
# Build Artifacts
|
||||
O.*_Common/
|
||||
O.*_RHEL8-x86_64/
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
@ -66,6 +69,7 @@ local.properties
|
||||
*.user
|
||||
*.sln.docstates
|
||||
*.sdf
|
||||
.cvsignore
|
||||
|
||||
#Test results
|
||||
*.log
|
||||
|
@ -2,7 +2,6 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=sinq
|
||||
LIBVERSION=koennecke
|
||||
BUILDCLASSES=Linux
|
||||
EPICS_VERSIONS=3.14.12 7.0.4.1
|
||||
|
||||
@ -32,5 +31,6 @@ 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
|
||||
|
@ -2,7 +2,7 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
|
||||
MODULE=sinq
|
||||
LIBVERSION=brambilla_m
|
||||
LIBVERSION=brambilla_m_test
|
||||
BUILDCLASSES=Linux
|
||||
EPICS_VERSIONS=3.14.12 7.0.4.1
|
||||
ARCH_FILTER=RHEL7-x86_64
|
||||
|
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
|
@ -38,3 +38,12 @@ Those political problems require a special development model:
|
||||
Take care of the sinqEPICsApp/src/sinq.dbd file. This is the one which differs mostly between
|
||||
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>
|
||||
```
|
||||
|
@ -4,6 +4,7 @@ record(motor,"$(P)$(M)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VMAX,"$(VMAX=0)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
|
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)
|
||||
}
|
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,84 +29,82 @@ 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;
|
||||
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++) {
|
||||
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()
|
||||
@ -116,27 +113,27 @@ void EL734Controller::switchRemote()
|
||||
size_t in, out;
|
||||
int reason;
|
||||
|
||||
strcpy(command,"RMT 1");
|
||||
strcpy(command, "RMT 1");
|
||||
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
strcpy(command,"ECHO 0");
|
||||
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");
|
||||
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");
|
||||
reply, COMLEN, 1., &out, &in, &reason);
|
||||
strcpy(command, "ECHO 0");
|
||||
pasynOctetSyncIO->writeRead(pasynUserController_, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
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;
|
||||
@ -145,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, 2.,&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;
|
||||
@ -208,14 +218,14 @@ 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;
|
||||
@ -224,58 +234,81 @@ EL734Axis::EL734Axis(EL734Controller *pC, int axisNo)
|
||||
/*
|
||||
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);
|
||||
} else {
|
||||
errlogPrintf("Bad response - %s - requesting limits at axis %d", reply, axisNo_);
|
||||
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 {
|
||||
errlogPrintf("Failed to read limits at axis %d", 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;
|
||||
errorReported = 0;
|
||||
errlogPrintf("Starting axis %d with destination %f", axisNo_,position/1000);
|
||||
sprintf(command, "p %d %.3f", axisNo_, position/1000.);
|
||||
status = pC_->transactController(axisNo_,command,reply);
|
||||
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;
|
||||
@ -285,7 +318,7 @@ 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);
|
||||
@ -296,49 +329,55 @@ asynStatus EL734Axis::home(double minVelocity, double maxVelocity, double accele
|
||||
|
||||
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);
|
||||
|
||||
|
||||
errorReported = 0;
|
||||
if (maxVelocity > 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 = asynSuccess;
|
||||
//static const char *functionName = "EL734Axis::stop";
|
||||
// static const char *functionName = "EL734Axis::stop";
|
||||
char command[COMLEN], reply[COMLEN];
|
||||
bool moving = false;
|
||||
|
||||
if(errorReported == 0){
|
||||
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;
|
||||
}
|
||||
@ -346,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, "U %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;
|
||||
@ -358,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.
|
||||
@ -369,32 +408,50 @@ 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;
|
||||
float low, high;
|
||||
asynStatus comStatus = asynSuccess;
|
||||
char command[COMLEN], reply[COMLEN], errTxt[256];
|
||||
|
||||
// protect against excessive polling
|
||||
if(time(NULL) < next_poll){
|
||||
if (time(NULL) < next_poll)
|
||||
{
|
||||
*moving = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
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){
|
||||
if (strstr(reply, "*ES") != NULL)
|
||||
{
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
@ -402,104 +459,126 @@ asynStatus EL734Axis::poll(bool *moving)
|
||||
updateMsgTxtFromDriver("Emergency Stop Engaged");
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
} else if(strstr(reply,"?BSY") != NULL){
|
||||
}
|
||||
else if (strstr(reply, "?BSY") != NULL)
|
||||
{
|
||||
*moving = true;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
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_);
|
||||
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 {
|
||||
errlogPrintf("Axis %d, reply %s, position %lf\n", axisNo_, reply, position);
|
||||
setDoubleParam(pC_->motorPosition_, position*1000);
|
||||
//setDoubleParam(pC_->motorEncoderPosition_, position);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// Read the moving status of this motor
|
||||
sprintf(command,"msr %d",axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_,command,reply);
|
||||
if(comStatus == asynError){
|
||||
setIntegerParam(pC_->motorStatusProblem_,true);
|
||||
sprintf(command, "msr %d", axisNo_);
|
||||
comStatus = pC_->transactController(axisNo_, command, reply);
|
||||
if (comStatus == asynError)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
goto skip;
|
||||
}
|
||||
sscanf(reply,"%x",&msr);
|
||||
sscanf(reply, "%x", &msr);
|
||||
|
||||
//errlogPrintf("Axis %d, reply %s, msr %d, oredmsr = %d, position = %lf\n",
|
||||
// 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_);
|
||||
// errlogPrintf("Axis %d finished\n", axisNo_);
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
|
||||
next_poll = time(NULL)+IDLEPOLL;
|
||||
if(oredMSR & 0x10){
|
||||
next_poll = time(NULL) + IDLEPOLL;
|
||||
if (oredMSR & 0x10)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, true);
|
||||
updateMsgTxtFromDriver("Lower Limit Hit");
|
||||
errorReported = 1;
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusLowLimit_, false);
|
||||
}
|
||||
if(oredMSR & 0x20){
|
||||
if (oredMSR & 0x20)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, true);
|
||||
updateMsgTxtFromDriver("Upper Limit Hit");
|
||||
errorReported = 1;
|
||||
comStatus = asynError;
|
||||
goto skip;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusHighLimit_, false);
|
||||
}
|
||||
if(homing){
|
||||
if (homing)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusAtHome_, true);
|
||||
}
|
||||
if(oredMSR & 0x1000){
|
||||
if (oredMSR & 0x1000)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
// errlogPrintf("Detected air cushion error on %d", axisNo_);
|
||||
errlogSevPrintf(errlogMajor, "Air cushion problem 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){
|
||||
if (oredMSR & 0x100)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Run failure at %d", axisNo_);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "Run failure at %d", axisNo_);
|
||||
updateMsgTxtFromDriver("Run failure");
|
||||
comStatus = asynError;
|
||||
errorReported = 1;
|
||||
goto skip;
|
||||
}
|
||||
if(oredMSR & 0x400){
|
||||
if (oredMSR & 0x400)
|
||||
{
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogSevPrintf(errlogMajor, "Positioning failure at %d", axisNo_);
|
||||
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) {
|
||||
errlogSevPrintf(errlogMinor, "Positioning fault at %d", axisNo_);
|
||||
}
|
||||
if (oredMSR & 0x200 || oredMSR & 0x80)
|
||||
{
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_WARNING, "Positioning fault at %d", axisNo_);
|
||||
}
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
*moving = true;
|
||||
next_poll = -1;
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
}
|
||||
}
|
||||
|
||||
skip:
|
||||
skip:
|
||||
callParamCallbacks();
|
||||
return comStatus;
|
||||
}
|
||||
@ -508,9 +587,9 @@ asynStatus EL734Axis::poll(bool *moving)
|
||||
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)
|
||||
{
|
||||
@ -522,6 +601,7 @@ static void EL734Register(void)
|
||||
iocshRegister(&EL734CreateControllerDef, EL734CreateContollerCallFunc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
epicsExportRegistrar(EL734Register);
|
||||
extern "C"
|
||||
{
|
||||
epicsExportRegistrar(EL734Register);
|
||||
}
|
||||
|
@ -28,31 +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 errorReported;
|
||||
|
||||
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();
|
||||
};
|
||||
|
@ -279,6 +279,7 @@ EuroMoveAxis::EuroMoveAxis(EuroMoveController *pC, int axisNo)
|
||||
pC_(pC)
|
||||
{
|
||||
motNo = axisNo;
|
||||
next_poll = -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -29,7 +29,7 @@ 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_;
|
||||
};
|
@ -138,7 +138,7 @@ NanotecAxis::NanotecAxis(NanotecController *pC, int axisNo, int busAddress)
|
||||
pC_(pC)
|
||||
{
|
||||
this->busAddress = busAddress;
|
||||
|
||||
next_poll = -1;
|
||||
}
|
||||
|
||||
|
||||
@ -377,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;
|
||||
|
||||
|
||||
@ -393,8 +393,14 @@ asynStatus NanotecAxis::poll(bool *moving)
|
||||
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);
|
||||
setDoubleParam(pC_->motorPosition_, (double)posVal);
|
||||
@ -407,8 +413,14 @@ asynStatus NanotecAxis::poll(bool *moving)
|
||||
if(comStatus) goto skip;
|
||||
|
||||
pPtr = strchr(reply,'$');
|
||||
pPtr++;
|
||||
statVal = atoi(pPtr);
|
||||
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);
|
||||
|
||||
|
@ -77,12 +77,27 @@ PhytronController::PhytronController(const char *portName, const char *PhytronPo
|
||||
pasynOctetSyncIO->setOutputEos(pasynUserController_,etx,strlen(etx));
|
||||
pasynOctetSyncIO->setInputEos(pasynUserController_,etx,strlen(etx));
|
||||
|
||||
new PhytronAxis(this, 1, encX);
|
||||
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
|
||||
@ -97,6 +112,14 @@ extern "C" int PhytronCreateController(const char *portName, const char *Phytron
|
||||
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
|
||||
@ -129,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
|
||||
@ -145,7 +185,7 @@ asynStatus PhytronController::transactController(int axisNo,char command[COMLEN]
|
||||
SINQAxis *axis = getAxis(axisNo);
|
||||
|
||||
|
||||
pasynOctetSyncIO->flush(pasynUserController_);
|
||||
/* pasynOctetSyncIO->flush(pasynUserController_); */
|
||||
|
||||
|
||||
memset(myCommand,0,sizeof(myCommand));
|
||||
@ -209,6 +249,7 @@ PhytronAxis::PhytronAxis(PhytronController *pC, int axisNo, int enc)
|
||||
}
|
||||
haveBrake = 0;
|
||||
brakeIO = -1;
|
||||
next_poll = -1;
|
||||
}
|
||||
|
||||
int PhytronAxis::setBrake(int brakeNO)
|
||||
@ -569,6 +610,79 @@ asynStatus PhytronAxis::poll(bool *moving)
|
||||
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 */
|
||||
@ -615,6 +729,21 @@ static void PhytronCreateContollerCallFunc(const iocshArgBuf *args)
|
||||
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};
|
||||
@ -634,6 +763,7 @@ static void phytronBrakeCallFunc(const iocshArgBuf *args)
|
||||
static void PhytronRegister(void)
|
||||
{
|
||||
iocshRegister(&PhytronCreateControllerDef, PhytronCreateContollerCallFunc);
|
||||
iocshRegister(&PhytronDoseCreateControllerDef, PhytronDoseCreateContollerCallFunc);
|
||||
iocshRegister(&phytronBrakeDef, phytronBrakeCallFunc);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,11 @@ 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 "SINQController.h"
|
||||
@ -17,6 +22,10 @@ Mark Koennecke, January 2019
|
||||
|
||||
#define COMLEN 80
|
||||
|
||||
|
||||
class PhytronController;
|
||||
class PhytronDoseController;
|
||||
|
||||
class PhytronAxis : public SINQAxis
|
||||
{
|
||||
public:
|
||||
@ -32,7 +41,7 @@ public:
|
||||
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 */
|
||||
@ -47,6 +56,21 @@ private:
|
||||
friend class PhytronController;
|
||||
};
|
||||
|
||||
class PhytronDoseAxis : public PhytronAxis
|
||||
{
|
||||
// A special version of PhytronAxis where the speed is controlled by the neutron flux.
|
||||
public:
|
||||
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,
|
||||
@ -57,7 +81,8 @@ public:
|
||||
PhytronAxis* getAxis(int axisNo);
|
||||
|
||||
friend class PhytronAxis;
|
||||
private:
|
||||
|
||||
protected:
|
||||
asynUser *pasynUserController_;
|
||||
|
||||
asynStatus transactController(int axisNo, char command[COMLEN], char reply[COMLEN]);
|
||||
@ -65,3 +90,20 @@ friend class PhytronAxis;
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -5,10 +5,16 @@
|
||||
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, const int& extraParams)
|
||||
: asynMotorController(portName, numAxes+1, NUM_MOTOR_DRIVER_PARAMS+extraParams,
|
||||
@ -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
|
||||
|
||||
|
@ -18,11 +18,15 @@ class epicsShareClass SINQController : public asynMotorController
|
||||
{
|
||||
public:
|
||||
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.....
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
@ -166,7 +168,7 @@ static void connectSlaveRecords(EL737priv *priv)
|
||||
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){
|
||||
@ -222,6 +224,7 @@ 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,
|
||||
@ -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,6 +346,8 @@ static asynStatus el737_transactCommand(EL737priv *priv,char command[COMLEN],cha
|
||||
|
||||
pasynOctetSyncIO->flush(priv->asynContext);
|
||||
|
||||
// errlogPrintf("EL737: sending command %s\n", command);
|
||||
|
||||
strcpy(message,"");
|
||||
status = pasynOctetSyncIO->writeRead(priv->asynContext, command, strlen(command),
|
||||
reply,COMLEN, 1.,&out,&in,&reason);
|
||||
@ -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,7 +419,8 @@ 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
|
||||
@ -434,7 +446,7 @@ static void runEvents(EL737priv *priv)
|
||||
}
|
||||
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);
|
||||
@ -449,36 +461,32 @@ static void runEvents(EL737priv *priv)
|
||||
|
||||
if(priv->sendThreshold == 1) {
|
||||
// fallback when we do not have the DB fields or threshCounter is invalid
|
||||
if(priv->presets[THRESHMON] <= 0) {
|
||||
errlogPrintf("Invalid threshold preset monitor %ld, no threshold sent", priv->presets[THRESHMON]);
|
||||
} else {
|
||||
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);
|
||||
// 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;
|
||||
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 */
|
||||
@ -490,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -505,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",
|
||||
@ -544,11 +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) {
|
||||
/*
|
||||
@ -556,7 +568,8 @@ static void el737Thread(void *param)
|
||||
*/
|
||||
runEvents(priv);
|
||||
if(priv->counting == 1){
|
||||
timeout = .1;
|
||||
timeout = .2;
|
||||
usleep(500);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@ -575,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);
|
||||
@ -624,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");
|
||||
@ -632,7 +645,7 @@ static void el737Thread(void *param)
|
||||
if(status != asynSuccess){
|
||||
errlogPrintf("devScalerEL737::el737Thread communication problem %s\n",
|
||||
priv->asynContext->errorMessage);
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/********************************************
|
||||
/*******************************************
|
||||
* pmacAxis.cpp
|
||||
*
|
||||
* PMAC Asyn motor based on the
|
||||
@ -16,7 +16,7 @@
|
||||
* when the motor is hung, it wont start. I check for this and cause an alarm.
|
||||
*
|
||||
* Another mode where the motor is in trouble is if it stays too long in status
|
||||
*5 or 6. I check and cause an alarm in this state too.
|
||||
* 5 or 6. I check and cause an alarm in this state too.
|
||||
*
|
||||
* Mark Koennecke, February 2013
|
||||
*
|
||||
@ -28,6 +28,14 @@
|
||||
*
|
||||
* Michele Brambilla, February 2022
|
||||
*
|
||||
* Added driver for special AMOR detector axes
|
||||
*
|
||||
* Mark Koennecke, June 2023
|
||||
*
|
||||
* Added driver for GirderAxis
|
||||
*
|
||||
* Stefan Mathis, July 2024
|
||||
*
|
||||
********************************************/
|
||||
|
||||
#include <epicsExit.h>
|
||||
@ -42,6 +50,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include<algorithm>
|
||||
#include<cmath>
|
||||
@ -180,7 +189,7 @@ asynStatus pmacAxis::getAxisInitialStatus(void) {
|
||||
cmdStatus = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if (cmdStatus) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s: Error: enaabling axis %d failed.\n", functionName,
|
||||
"%s: Error: enabling axis %d failed.\n", functionName,
|
||||
axisNo_);
|
||||
return asynError;
|
||||
}
|
||||
@ -219,6 +228,11 @@ asynStatus pmacAxis::move(double position, int relative, double min_velocity,
|
||||
status6Time = 0;
|
||||
starting = 1;
|
||||
|
||||
/* set speed first */
|
||||
sprintf(command, "Q%d04=%f", axisNo_, max_velocity);
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
|
||||
/* send drive command */
|
||||
sprintf(command, "P%2.2d23=0 Q%2.2d01=%12.4f M%2.2d=1", axisNo_, axisNo_,
|
||||
realPosition, axisNo_);
|
||||
|
||||
@ -277,17 +291,22 @@ asynStatus pmacAxis::setPosition(double position) {
|
||||
}
|
||||
|
||||
asynStatus pmacAxis::stop(double acceleration) {
|
||||
asynStatus status = asynError;
|
||||
asynStatus status = asynSuccess;
|
||||
static const char *functionName = "pmacAxis::stopAxis";
|
||||
bool moving = false;
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
char command[128] = {0};
|
||||
char response[32] = {0};
|
||||
|
||||
sprintf(command, "M%2.2d=8", axisNo_);
|
||||
this->getAxisStatus(&moving);
|
||||
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if(moving) {
|
||||
// only send a stop when actually moving
|
||||
sprintf(command, "M%2.2d=8", axisNo_);
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -339,7 +358,7 @@ static char *translateAxisError(int axErr) {
|
||||
return strdup("command not allowed");
|
||||
break;
|
||||
case 6:
|
||||
return strdup("watchdog triggere");
|
||||
return strdup("watchdog triggered");
|
||||
break;
|
||||
case 7:
|
||||
return strdup("current limit reached");
|
||||
@ -474,7 +493,6 @@ asynStatus pmacAxis::getAxisStatus(bool *moving) {
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusMoving_, false);
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
errlogPrintf("Axis %d did not start within 10 seconds!! BROKEN\n", axisNo_);
|
||||
updateMsgTxtFromDriver("Axis did not start within 10 seconds");
|
||||
starting = 0;
|
||||
@ -582,7 +600,7 @@ asynStatus pmacHRPTAxis::getAxisStatus(bool *moving) {
|
||||
char response[pC_->PMAC_MAXBUF_];
|
||||
int cmdStatus = 0, nvals, crashSignal;
|
||||
|
||||
asynStatus result = pmacAxis::getAxisStatus(moving);
|
||||
asynStatus result = pmacV3Axis::getAxisStatus(moving);
|
||||
sprintf(command, "P%2.2d53", axisNo_);
|
||||
cmdStatus = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
nvals = sscanf(response, "%d", &crashSignal);
|
||||
@ -599,7 +617,6 @@ asynStatus pmacHRPTAxis::getAxisStatus(bool *moving) {
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusMoving_, false);
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -618,7 +635,7 @@ asynStatus pmacAxis::enable(int on) {
|
||||
* ======================================================*/
|
||||
SeleneAxis::SeleneAxis(SeleneController *pC, int axisNo, double limitTarget)
|
||||
: pmacAxis(pC, axisNo, false) {
|
||||
static const char *functionName = "pmacAxis::pmacAxis";
|
||||
static const char *functionName = "SeleneAxis::SeleneAxis";
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
@ -892,15 +909,10 @@ pmacV3Axis::pmacV3Axis(pmacController *pController, int axisNo)
|
||||
};
|
||||
|
||||
asynStatus pmacV3Axis::poll(bool *moving) {
|
||||
int status = 0;
|
||||
asynStatus status = asynSuccess;
|
||||
static const char *functionName = "pmacV3Axis::poll";
|
||||
char message[132];
|
||||
|
||||
// Protect against excessive polling
|
||||
if (time(NULL) < next_poll) {
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
sprintf(message, "%s: Polling axis: %d", functionName, this->axisNo_);
|
||||
pC_->debugFlow(message);
|
||||
|
||||
@ -912,14 +924,9 @@ asynStatus pmacV3Axis::poll(bool *moving) {
|
||||
functionName, pC_->portName, axisNo_);
|
||||
}
|
||||
|
||||
if (*moving) {
|
||||
next_poll = time(NULL) + BUSYPOLL;
|
||||
} else {
|
||||
next_poll = time(NULL) + IDLEPOLL;
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status ? asynError : asynSuccess;
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -944,14 +951,16 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
|
||||
if (cmdStatus || nvals != 3) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"drvPmacAxisGetStatus: Failed to read position and status, "
|
||||
"Status: %d\nCommand :%s\nResponse:%s\n",
|
||||
cmdStatus, command, response);
|
||||
"Status: %d\nCommand :%s\nParsed: %d\nResponse:%s\n",
|
||||
cmdStatus, command, nvals, response);
|
||||
updateMsgTxtFromDriver("Cannot read Axis position and status");
|
||||
}
|
||||
|
||||
pmacV3Controller *p3C_ = (pmacV3Controller *)pC_;
|
||||
IsEnable = axStat != -3;
|
||||
|
||||
asynStatus st = setIntegerParam(p3C_->axisEnabled_, axStat > -1);
|
||||
setIntegerParam(p3C_->axisEnabled_, IsEnable);
|
||||
asynStatus st = setIntegerParam(p3C_->enableAxis_, IsEnable);
|
||||
cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
|
||||
int direction = 0;
|
||||
@ -977,36 +986,17 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
|
||||
previous_position_ = position;
|
||||
previous_direction_ = direction;
|
||||
|
||||
/* are we done? */
|
||||
if ((axStat == 0 || axStat < 0) && starting == 0) {
|
||||
if (axStat <= 0 && starting == 0) {
|
||||
done = 1;
|
||||
} else {
|
||||
starting = 0;
|
||||
done = 0;
|
||||
}
|
||||
|
||||
// if (starting && time(NULL) > startTime + 10) {
|
||||
// /*
|
||||
// did not start in 10 seconds: messed up: cause an alarm
|
||||
// */
|
||||
// done = 1;
|
||||
// *moving = false;
|
||||
// st = setIntegerParam(pC_->motorStatusMoving_, false);
|
||||
// cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
// st = setIntegerParam(pC_->motorStatusDone_, true);
|
||||
// cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
// st = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
// cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
// errlogPrintf("Axis %d did not start within 10 seconds!! BROKEN\n", axisNo_);
|
||||
// updateMsgTxtFromDriver("Axis did not start within 10 seconds");
|
||||
// starting = 0;
|
||||
// return st;
|
||||
// }
|
||||
|
||||
// /*
|
||||
// code to test against too long status 5 or 6
|
||||
// */
|
||||
int EstimatedTimeOfArrival = 60;
|
||||
int EstimatedTimeOfArrival = 120;
|
||||
if (axStat == 5 || axStat == 6) {
|
||||
if (status6Time == 0) {
|
||||
status6Time = time(NULL);
|
||||
@ -1014,20 +1004,11 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
|
||||
EstimatedTimeOfArrival =
|
||||
std::ceil(2 * std::fabs(setpointPosition_ - position) / Speed);
|
||||
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (time(NULL) > status6Time + EstimatedTimeOfArrival) {
|
||||
/* trigger error only when not moving */
|
||||
if (abs(position - statusPos) < .1) {
|
||||
// done = 1;
|
||||
// *moving = false;
|
||||
// st = setIntegerParam(pC_->motorStatusMoving_, false);
|
||||
// cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
// st = setIntegerParam(pC_->motorStatusDone_, true);
|
||||
// cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
// st = setIntegerParam(pC_->motorStatusProblem_, true);
|
||||
// cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
|
||||
done = 1;
|
||||
errlogPrintf(
|
||||
"Axis %d stayed in 5,6 for more than %d seconds BROKEN\n",
|
||||
axisNo_, EstimatedTimeOfArrival);
|
||||
@ -1082,6 +1063,7 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
|
||||
cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
|
||||
nvals = sscanf(response, "%d %d %d", &axErr, &highLim, &lowLim);
|
||||
|
||||
if (st || nvals != 3) {
|
||||
asynPrint(
|
||||
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
@ -1092,9 +1074,6 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
|
||||
"Cannot read Axis Error, high and low limit flag Status");
|
||||
}
|
||||
|
||||
// errlogPrintf("Polling, axStat = %d, axErr = %d, position = %f\n", axStat,
|
||||
// axErr, position);
|
||||
|
||||
sprintf(message, "poll results: axis %d, status %d, axErr %d, done = %d",
|
||||
axisNo_, axStat, axErr, done);
|
||||
pC_->debugFlow(message);
|
||||
@ -1148,6 +1127,40 @@ asynStatus pmacV3Axis::getAxisStatus(bool *moving) {
|
||||
axisProblemFlag = 0;
|
||||
axisErrorCount = 0;
|
||||
}
|
||||
|
||||
// test for more error conditions encoded in axStat
|
||||
switch(axStat) {
|
||||
case -4:
|
||||
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
||||
"Emergency stop activated", axisNo_);
|
||||
updateMsgTxtFromDriver(message);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
||||
axisProblemFlag = 1;
|
||||
break;
|
||||
case -5:
|
||||
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
||||
"Motor is not installed", axisNo_);
|
||||
updateMsgTxtFromDriver(message);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
||||
axisProblemFlag = 1;
|
||||
break;
|
||||
case -3:
|
||||
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
||||
"Axis disabled", axisNo_);
|
||||
updateMsgTxtFromDriver(message);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
||||
axisProblemFlag = 1;
|
||||
break;
|
||||
case -2:
|
||||
case -1:
|
||||
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
||||
"Axis locked", axisNo_);
|
||||
updateMsgTxtFromDriver(message);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
||||
axisProblemFlag = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
st = setIntegerParam(pC_->motorStatusProblem_, axisProblemFlag);
|
||||
cmdStatus = cmdStatus > st ? cmdStatus : st;
|
||||
|
||||
@ -1163,4 +1176,580 @@ asynStatus pmacV3Axis::move(double position, int relative, double min_velocity,
|
||||
}
|
||||
updateMsgTxtFromDriver("");
|
||||
return pmacAxis::move(position, relative, min_velocity, max_velocity, acceleration);
|
||||
}
|
||||
}
|
||||
|
||||
/*========================= Amor Detector Motor code ==============*/
|
||||
AmorDetectorAxis::AmorDetectorAxis(pmacController *pC, int axisNo, int function)
|
||||
: pmacAxis(pC, axisNo, false) {
|
||||
static const char *functionName = "pmacAxis::pmacAxis";
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
_function = function;
|
||||
|
||||
callParamCallbacks();
|
||||
|
||||
/* Wake up the poller task which will make it do a poll,
|
||||
* updating values for this axis to use the new resolution (stepSize_) */
|
||||
pC_->wakeupPoller();
|
||||
}
|
||||
/*-----------------------------------------------------------------*/
|
||||
asynStatus AmorDetectorAxis::moveVelocity(double min_velocity,
|
||||
double max_velocity,
|
||||
double acceleration)
|
||||
{
|
||||
updateMsgTxtFromDriver("Function moveVelocity not allowed");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
asynStatus AmorDetectorAxis::home(double min_velocity,
|
||||
double max_velocity,
|
||||
double acceleration,
|
||||
int forwards)
|
||||
{
|
||||
updateMsgTxtFromDriver("Cannot home AMOR detector motors");
|
||||
return asynError;
|
||||
}
|
||||
/*-------------------------------------------------------------------*/
|
||||
asynStatus AmorDetectorAxis::setPosition(double position)
|
||||
{
|
||||
updateMsgTxtFromDriver("Cannot setPosition() AMOR detector motors");
|
||||
return asynError;
|
||||
}
|
||||
/*-------------------------------------------------------------------*/
|
||||
asynStatus AmorDetectorAxis::move(double position, int relative,
|
||||
double min_velocity, double max_velocity,
|
||||
double acceleration)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
static const char *functionName = "AmorDetectorAxis::move";
|
||||
double realPosition;
|
||||
int parkStatus;
|
||||
time_t errorWait;
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
char command[128] = {0};
|
||||
char response[32] = {0};
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
/* test if we are not in the change position */
|
||||
sprintf(command,"P358");
|
||||
pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
sscanf(response, "%d", &parkStatus);
|
||||
if(_function != ADPARK && parkStatus == 2) {
|
||||
updateMsgTxtFromDriver("Cannot run motor when in park position");
|
||||
errlogPrintf("Cannot run motor when in park position");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
if (relative) {
|
||||
realPosition = previous_position_ + position / MULT;
|
||||
} else {
|
||||
realPosition = position / MULT;
|
||||
}
|
||||
|
||||
/* set target */
|
||||
errlogPrintf("function %d, sending position %f\n", _function, realPosition);
|
||||
|
||||
switch(_function){
|
||||
case ADCOM:
|
||||
sprintf(command, "Q451=%f", realPosition);
|
||||
break;
|
||||
case ADCOZ:
|
||||
sprintf(command, "Q454=%f", realPosition);
|
||||
break;
|
||||
case ADPARK:
|
||||
// test if a reset is required first
|
||||
sprintf(command, "P359");
|
||||
pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
sscanf(response, "%d", &parkStatus);
|
||||
errlogPrintf("park status %d\n", parkStatus);
|
||||
if(parkStatus != 0){
|
||||
sprintf(command,"P352=2");
|
||||
pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
errlogPrintf("Park status %d, error reset: %s\n", parkStatus, command);
|
||||
}
|
||||
errorWait = time(NULL);
|
||||
while(true){
|
||||
usleep(5000);
|
||||
sprintf(command, "P359");
|
||||
pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
sscanf(response, "%d", &parkStatus);
|
||||
errlogPrintf("P359 = %d\n", parkStatus);
|
||||
if(parkStatus == 0){
|
||||
break;
|
||||
}
|
||||
if(time(NULL) > errorWait + 3){
|
||||
errlogPrintf("Failed to clear error status after 3 seconds\n");
|
||||
updateMsgTxtFromDriver("Failed to clear errorState in 3 seconds");
|
||||
return asynError;
|
||||
}
|
||||
}
|
||||
// now drive to the real place
|
||||
if(realPosition == 0) {
|
||||
sprintf(command, "P350=3");
|
||||
} else if(realPosition < -90){
|
||||
sprintf(command, "P350=2");
|
||||
} else {
|
||||
updateMsgTxtFromDriver("Can only reach -100, 0, nothing else");
|
||||
return asynError;
|
||||
}
|
||||
errlogPrintf("Park position %f, park command: %s\n", realPosition, command);
|
||||
|
||||
}
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if(status != asynSuccess) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"AmorDetectorAxis::move failed to send command %s", command);
|
||||
updateMsgTxtFromDriver("Cannot send Amor Detector Command");
|
||||
return status;
|
||||
}
|
||||
|
||||
/* send move command */
|
||||
if(_function != ADPARK) {
|
||||
sprintf(command, "P350=1");
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if(status != asynSuccess) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"AmorDetectorAxis::move failed to send command %s", command);
|
||||
updateMsgTxtFromDriver("Cannot send Amor Detector Command");
|
||||
}
|
||||
}
|
||||
|
||||
det_starting = true;
|
||||
det_startTime = time(NULL);
|
||||
|
||||
next_poll = BUSYPOLL;
|
||||
|
||||
return status;
|
||||
}
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
asynStatus AmorDetectorAxis::stop(double acceleration) {
|
||||
asynStatus status = asynError;
|
||||
static const char *functionName = "AmorDetectorAxis::stopAxis";
|
||||
bool moving = false;
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
char command[128] = {0};
|
||||
char response[32] = {0};
|
||||
|
||||
this->poll(&moving);
|
||||
det_starting = false;
|
||||
|
||||
if(moving) {
|
||||
// only send a stop when actually moving
|
||||
sprintf(command, "P350=8");
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
asynStatus AmorDetectorAxis::poll(bool *moving)
|
||||
{
|
||||
asynStatus status = asynError;
|
||||
static const char *functionName = "AmorDetectorAxis::poll";
|
||||
int targetReached, errorCode, parkCode;
|
||||
double position;
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
char command[128] = {0};
|
||||
char response[32] = {0};
|
||||
char errorMessage[128] = {0};
|
||||
|
||||
/* read in position flag*/
|
||||
sprintf(command, "P354");
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if(status != asynSuccess){
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"AmorDetectorAxis::poll failed to read in position");
|
||||
updateMsgTxtFromDriver("Failed to read Amor Detector in position");
|
||||
return status;
|
||||
}
|
||||
sscanf(response, "%d", &targetReached);
|
||||
|
||||
//if(_function == ADCOM){
|
||||
// errlogPrintf("startTime: %ld, current time: %ld, starting flag %d, targetReached %d\n", det_startTime, time(NULL),
|
||||
// det_starting, targetReached);
|
||||
//}
|
||||
|
||||
// The thing is sometimes slow to start, allow 7 seconds
|
||||
// Especially coz is really slow to indicate
|
||||
if(det_starting && time(NULL) > (det_startTime + 7)) {
|
||||
det_starting = false;
|
||||
//if(_function == ADCOM){
|
||||
// errlogPrintf("Resetting starting flag after timeout\n");
|
||||
//}
|
||||
}
|
||||
|
||||
setIntegerParam(pC_->motorStatusProblem_, false);
|
||||
if(targetReached == 0 && det_starting == false){
|
||||
*moving = false;
|
||||
setIntegerParam(pC_->motorStatusDone_, true);
|
||||
next_poll = IDLEPOLL;
|
||||
} else {
|
||||
*moving = true;
|
||||
if(targetReached == 1){
|
||||
det_starting = false;
|
||||
//if(_function == ADCOM){
|
||||
// errlogPrintf("Resetting starting flag following targetReached flag\n");
|
||||
//}
|
||||
}
|
||||
setIntegerParam(pC_->motorStatusDone_, false);
|
||||
}
|
||||
|
||||
// read position
|
||||
if(_function == ADCOM) {
|
||||
strcpy(command,"Q0510");
|
||||
} else if(_function == ADCOZ) {
|
||||
strcpy(command, "Q454");
|
||||
} else if(_function == ADPARK){
|
||||
strcpy(command,"P358");
|
||||
}
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if(status != asynSuccess){
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"AmorDetectorAxis::poll failed to read value");
|
||||
updateMsgTxtFromDriver("Failed to read Amor Detector value");
|
||||
return status;
|
||||
}
|
||||
if(_function != ADPARK){
|
||||
sscanf(response, "%lf", &position);
|
||||
} else {
|
||||
// analyse the park code to check where the thing is. This does
|
||||
// not show good values while driving. But that is a limitation
|
||||
// of the hardware.
|
||||
sscanf(response, "%d", &parkCode);
|
||||
if(parkCode == 1) {
|
||||
position = 0;
|
||||
} else {
|
||||
position = -100;
|
||||
}
|
||||
// errlogPrintf("parkCode %d , position %f,targetREached = %d\n", parkCode, position, targetReached);
|
||||
}
|
||||
setDoubleParam(pC_->motorPosition_, position * 1000.);
|
||||
setDoubleParam(pC_->motorEncoderPosition_, position * 1000.);
|
||||
|
||||
//if(_function == ADCOM){
|
||||
// errlogPrintf("polling for function %d, position %f, pos*10^3 %f, targetREached = %d\n", _function, position,
|
||||
// position*MULT, targetReached);
|
||||
// }
|
||||
|
||||
// read error status
|
||||
sprintf(command, "P359");
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if(status == asynSuccess) {
|
||||
sscanf(response, "%d", &errorCode);
|
||||
switch(errorCode) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
strcpy(errorMessage,"COZ not released");
|
||||
break;
|
||||
case 2:
|
||||
strcpy(errorMessage,"Motor is in wrong state");
|
||||
break;
|
||||
case 3:
|
||||
strcpy(errorMessage,"FTZ error while moving");
|
||||
break;
|
||||
case 4:
|
||||
strcpy(errorMessage, "Break while moving");
|
||||
break;
|
||||
case 5:
|
||||
strcpy(errorMessage, "No freigabe during move");
|
||||
break;
|
||||
default:
|
||||
strcpy(errorMessage, "Unknown error code from: ");
|
||||
strcat(errorMessage, response);
|
||||
break;
|
||||
}
|
||||
if(strlen(errorMessage) > 1) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"AmorDetectorAxis::poll: %s", errorMessage);
|
||||
updateMsgTxtFromDriver(errorMessage);
|
||||
status = asynError;
|
||||
}
|
||||
}
|
||||
|
||||
callParamCallbacks();
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
|
||||
GirderAxis::GirderAxis(pmacController *pController, int axisNo)
|
||||
: pmacV3Axis(pController, axisNo){
|
||||
|
||||
// GirderAxis expects a pmacV3Controller. Therefore it is checked whether the pointer pController can be cast
|
||||
// to this object type. If this is not possible, the user made an error in the configuration files. This is documented
|
||||
// in a corresponding error; after that, an exception is thrown to avoid returning an "illegal" instance of GirderAxis.
|
||||
pmacV3Controller* pV3Controller = dynamic_cast<pmacV3Controller*>(pController);
|
||||
if (pV3Controller == nullptr) {
|
||||
errlogPrintf("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files.");
|
||||
throw std::invalid_argument("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files.");
|
||||
}
|
||||
|
||||
// Initial values for member variables
|
||||
next_poll = -1;
|
||||
previous_position_ = 0.0;
|
||||
previous_direction_ = 0;
|
||||
status6Time = 0;
|
||||
statusPos = 0.0;
|
||||
homing = 0;
|
||||
axisErrorCount = 0;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
asynStatus GirderAxis::move(double position, int relative, double min_velocity, double max_velocity, double acceleration) {
|
||||
|
||||
// If the axis is not enabled, do nothing
|
||||
if(!IsEnable) {
|
||||
updateMsgTxtFromDriver("Error: axis disabled");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// =======================================
|
||||
// Local variable declaration
|
||||
|
||||
asynStatus status = asynError;
|
||||
char command[pC_->PMAC_MAXBUF_] = {0};
|
||||
char response[pC_->PMAC_MAXBUF_] = {0};
|
||||
double realPosition = 0;
|
||||
|
||||
// =======================================
|
||||
|
||||
updateMsgTxtFromDriver("");
|
||||
static const char *functionName = "GirderAxis::move";
|
||||
pC_->debugFlow(functionName);
|
||||
|
||||
if (relative) {
|
||||
realPosition = previous_position_ + position / MULT;
|
||||
} else {
|
||||
realPosition = position / MULT;
|
||||
}
|
||||
startTime = time(NULL);
|
||||
status6Time = 0;
|
||||
starting = 1;
|
||||
|
||||
// Set target position
|
||||
snprintf(command, sizeof(command), "Q251=%f", realPosition);
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if (status == asynError) {
|
||||
updateMsgTxtFromDriver("Error: Could not set target position");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Start motion
|
||||
snprintf(command, sizeof(command), "P150=1");
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
if (status == asynError) {
|
||||
updateMsgTxtFromDriver("Error: Could not start motion");
|
||||
return asynError;
|
||||
}
|
||||
|
||||
next_poll = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus GirderAxis::stop(double acceleration) {
|
||||
|
||||
// =======================================
|
||||
// Local variable declaration
|
||||
|
||||
asynStatus status = asynSuccess;
|
||||
static const char *functionName = "GirderAxis::stop";
|
||||
bool moving = false;
|
||||
char command[pC_->PMAC_MAXBUF_] = {0};
|
||||
char response[pC_->PMAC_MAXBUF_] = {0};
|
||||
|
||||
// =======================================
|
||||
|
||||
pC_->debugFlow(functionName);
|
||||
this->poll(&moving);
|
||||
|
||||
if(moving) {
|
||||
// only send a stop when actually moving
|
||||
snprintf(command, sizeof(command), "P150=8");
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
asynStatus GirderAxis::poll(bool *moving) {
|
||||
|
||||
// =======================================
|
||||
// Local variable declaration
|
||||
|
||||
asynStatus status = asynSuccess;
|
||||
static const char *functionName = "GirderAxis::poll";
|
||||
char command[pC_->PMAC_MAXBUF_] = {0};
|
||||
char response[pC_->PMAC_MAXBUF_] = {0};
|
||||
char message[132], *axMessage;
|
||||
double position = 0;
|
||||
int moving_to_position = 0, nvals = 0, axisProblemFlag = 0, axStat = 0, axError = 0, isEnabled = 1;
|
||||
asynStatus st;
|
||||
|
||||
// =======================================
|
||||
|
||||
// Check for girder axis specific errors
|
||||
snprintf(command, sizeof(command), "P159");
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
sscanf(response, "P159=%d", &axError);
|
||||
switch(axError) {
|
||||
case 1:
|
||||
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
||||
"Axis not switched on", axisNo_);
|
||||
updateMsgTxtFromDriver(message);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
||||
axisProblemFlag = 1;
|
||||
isEnabled = 0;
|
||||
break;
|
||||
case 2:
|
||||
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
||||
"Axis not ready for new command", axisNo_);
|
||||
updateMsgTxtFromDriver(message);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
||||
axisProblemFlag = 1;
|
||||
break;
|
||||
case 3:
|
||||
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
||||
"Axis 1 ERROR during motion", axisNo_);
|
||||
updateMsgTxtFromDriver(message);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
||||
axisProblemFlag = 1;
|
||||
break;
|
||||
case 4:
|
||||
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
||||
"Axis 2 ERROR during motion", axisNo_);
|
||||
updateMsgTxtFromDriver(message);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
||||
axisProblemFlag = 1;
|
||||
break;
|
||||
}
|
||||
st = setIntegerParam(pC_->motorStatusProblem_, axisProblemFlag);
|
||||
status = status > st ? status : st;
|
||||
|
||||
// Downcast the pointer pmacController to pmacV3Controller in a typesafe manner
|
||||
pmacV3Controller* p3C_ = dynamic_cast<pmacV3Controller*>(pC_);
|
||||
if (p3C_ == nullptr) {
|
||||
errlogPrintf("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files.");
|
||||
throw std::invalid_argument("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files.");
|
||||
}
|
||||
setIntegerParam(p3C_->axisEnabled_, isEnabled);
|
||||
|
||||
st = setIntegerParam(p3C_->enableAxis_, isEnabled);
|
||||
status = status > st ? status : st;
|
||||
|
||||
int direction = 0;
|
||||
|
||||
/*
|
||||
In GirderAxis::move, the user input is scaled by /MULT. Hence, the output of
|
||||
Qxx10 needs to be scaled by *MULT
|
||||
*/
|
||||
st = setDoubleParam(pC_->motorPosition_, position * MULT);
|
||||
status = status > st ? status : st;
|
||||
st = setDoubleParam(pC_->motorEncoderPosition_, position * MULT);
|
||||
status = status > st ? status : st;
|
||||
|
||||
// Calculate the current (or last) movement direction
|
||||
if ((position - previous_position_) > 0) {
|
||||
direction = 1;
|
||||
} else if (position - previous_position_ == 0.0) {
|
||||
direction = previous_direction_;
|
||||
} else {
|
||||
direction = 0;
|
||||
}
|
||||
st = setIntegerParam(pC_->motorStatusDirection_, direction);
|
||||
status = status > st ? status : st;
|
||||
|
||||
// Store the position which was read out from the hardware together with the calculated direction for the next poll
|
||||
previous_position_ = position;
|
||||
previous_direction_ = direction;
|
||||
|
||||
// Is the axis currently moving?
|
||||
snprintf(command, sizeof(command), "P154");
|
||||
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
||||
sscanf(response, "P154=%d", &moving_to_position);
|
||||
|
||||
/*
|
||||
This code tests whether the axis is too long in status 5 or 6.
|
||||
If the axis is in status 5 or 6, a time counter (status6Time) is started and updated during subsequent polls.
|
||||
If status6Time exceeds the estimated arrival time of 120 seconds, a corresponding error is returned.
|
||||
*/
|
||||
int EstimatedTimeOfArrival = 120; // seconds
|
||||
if (axStat == 5 || axStat == 6) {
|
||||
if (status6Time == 0) {
|
||||
status6Time = time(nullptr);
|
||||
statusPos = position;
|
||||
} else {
|
||||
if (time(nullptr) > status6Time + EstimatedTimeOfArrival) {
|
||||
/* trigger error only when not moving */
|
||||
if (abs(position - statusPos) < .1) {
|
||||
moving_to_position = 0;
|
||||
errlogPrintf(
|
||||
"Axis %d stayed in status 5 or 6 for more than %d seconds BROKEN\n",
|
||||
axisNo_, EstimatedTimeOfArrival);
|
||||
updateMsgTxtFromDriver("Axis stayed in status 5 or 6 for more than estimated time: BROKEN");
|
||||
status6Time = 0;
|
||||
return status;
|
||||
} else {
|
||||
status6Time = time(nullptr);
|
||||
statusPos = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If the axis is moving, set the corresponding flags in the pmacController (pC_) and end the poll.
|
||||
*/
|
||||
if (!moving_to_position) {
|
||||
*moving = true;
|
||||
st = setIntegerParam(pC_->motorStatusMoving_, true);
|
||||
status = status > st ? status : st;
|
||||
st = setIntegerParam(pC_->motorStatusDone_, false);
|
||||
return status > st ? status : st;
|
||||
} else {
|
||||
*moving = false;
|
||||
st = setIntegerParam(pC_->motorStatusMoving_, false);
|
||||
status = status > st ? status : st;
|
||||
st = setIntegerParam(pC_->motorStatusDone_, true);
|
||||
status = status > st ? status : st;
|
||||
}
|
||||
|
||||
/* Set any axis specific general problem bits. */
|
||||
if (axError != 0) {
|
||||
axisProblemFlag = 1;
|
||||
if (axisErrorCount < 10) {
|
||||
axMessage = translateAxisError(axError);
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"drvGirderAxisGetStatus: Axis %d is in deep trouble: axis error "
|
||||
"code %d, translated: %s:, status code = %d\n",
|
||||
axisNo_, axError, axMessage, axStat);
|
||||
snprintf(message, sizeof(message), "GirderAxis error: %s", axMessage);
|
||||
updateMsgTxtFromDriver(message);
|
||||
if (axMessage != NULL) {
|
||||
free(axMessage);
|
||||
}
|
||||
axisErrorCount++;
|
||||
} else if (axisErrorCount == 10) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Suppressing further axis error messages\n");
|
||||
axisErrorCount++;
|
||||
}
|
||||
} else {
|
||||
axisProblemFlag = 0;
|
||||
axisErrorCount = 0;
|
||||
}
|
||||
|
||||
st = setIntegerParam(pC_->motorStatusProblem_, axisProblemFlag);
|
||||
status = status > st ? status : st;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -70,22 +70,6 @@ protected:
|
||||
friend class pmacController;
|
||||
friend class pmacV3Controller;
|
||||
};
|
||||
/*----------------------------------------------------------------------------------------------*/
|
||||
class pmacHRPTAxis : public pmacAxis
|
||||
{
|
||||
public:
|
||||
pmacHRPTAxis(pmacController *pController, int axisNo) : pmacAxis(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;
|
||||
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------------------------*/
|
||||
class SeleneAxis : public pmacAxis
|
||||
{
|
||||
@ -157,4 +141,69 @@ public:
|
||||
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
@ -140,6 +140,8 @@ class pmacController : public SINQController {
|
||||
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)
|
||||
@ -166,12 +168,14 @@ class SeleneController : public pmacController {
|
||||
|
||||
#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 = 2);
|
||||
double idlePollPeriod, const int &extraParams = 4);
|
||||
|
||||
// overloaded because we want to enable/disable the motor
|
||||
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
|
||||
@ -181,12 +185,15 @@ public:
|
||||
|
||||
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 */
|
||||
|
@ -7,6 +7,8 @@ registrar(EuroMoveRegister)
|
||||
registrar(NanotecRegister)
|
||||
registrar(pmacControllerRegister)
|
||||
registrar(pmacAsynIPPortRegister)
|
||||
registrar(MasterMACSRegister)
|
||||
registrar(SINQControllerRegister)
|
||||
|
||||
#--------------------------------------------------------
|
||||
# With the PSI module build system, including these items actually
|
||||
@ -18,6 +20,7 @@ registrar(pmacAsynIPPortRegister)
|
||||
#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"
|
||||
|
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