39 Commits

Author SHA1 Message Date
848da9dfe3 SINQSW-107 adds missing commands that were in previous version 2024-10-18 09:22:10 +02:00
af61358351 SINQSW-107 draft streamdevice based counterbox driver 2024-10-17 17:09:49 +02:00
39098fd0d1 Adds .clang-format style for formatting files 2024-09-25 16:21:01 +02:00
1539bfc66a bugfix: don't need to run on initialisation 2024-09-23 09:04:08 +02:00
d88e5877a7 Bugfix: A disabled PMac is no longer consider moving 2024-09-18 10:57:57 +02:00
118e177e04 Bugfix: A disabled PMac is no longer consider moving 2024-09-18 10:48:59 +02:00
f61daf0b49 just adds some warnings for potentially incorrect code 2024-09-11 15:07:25 +02:00
6dbd0fc0d7 Merge branch 'SINQSW-86-read-after-motor-reconnection' into 'master'
SINQSW-86 fix build error

See merge request sinqdev/sinqepicsapp!2
2024-09-10 09:21:15 +02:00
e657ea675c SINQSW-86 fix build error 2024-09-10 09:21:15 +02:00
5c0c917be9 Merge branch 'initializer_for_EL734Axis' into 'master'
Girder axis and initializer EL734Axis

See merge request sinqdev/sinqepicsapp!3
2024-09-10 09:19:24 +02:00
35c12274b0 Merge branch 'master' into 'initializer_for_EL734Axis'
# Conflicts:
#   sinqEPICSApp/src/EL734Driver.cpp
2024-09-10 09:19:06 +02:00
a6a8f14b26 The member variable oredMSR of EL734Axis contains the axis status from the previous poll.
Therefore, it needs to initialized with a sensible value (i.e. 1 which
means that the axis is standing still and has no errors.
2024-09-10 08:58:59 +02:00
33f118ce1e Fixed type in pmacController.cpp 2024-08-20 11:14:18 +02:00
adf8b30692 Minor changes to SINQ drivers:
- Fixed enable/disable at MasterMACS
2024-08-20 11:12:18 +02:00
c972cce072 Added girder translation axis drivers 2024-07-18 13:43:12 +02:00
64c8b08ce4 Fixed an initialisation bug in EL734Driver which prevented motors from
being polled. The value in question was next_poll which is now set to -1
in the constructor.
2024-06-07 13:36:34 +02:00
30228adf50 - Fixed a enable PV initialisation bug in MasterMACS
- Made the HRPT axis base itself on V3 of the pmacAxis
- Improved and added utils programs
2024-06-07 08:58:53 +02:00
d9d6dae19f Updated the sync utilities 2024-05-23 16:00:32 +02:00
32a8c27dbf Merge branch 'speed' 2024-02-23 11:34:26 +01:00
235e403fb3 - Added Makefile.RHEL8 for RHEL8
- Added a interMessagePeriod to SINQController, pmacController and MasterMACSController. Also a iocsh command to modify this at runtime
2024-02-23 11:30:46 +01:00
b4e201ae86 Final version of the SINQ EPICS module for RHEL7 in 2023
- Fixes to MasterMACS, mostly.
- Minor changes
2024-01-11 14:05:49 +01:00
9422353107 Alex: Committing Mark's fixes to el737 devsup 2023-09-12 16:43:24 +02:00
e3ac2962f5 - Many improvements to the MasterMACS driver
- Slowing down the pmac driver
- Fixing a bug in the Nanotec driver which caused an IOC crash when the
  motor sends bad data
2023-08-28 15:01:48 +02:00
7a81e2c5a0 Reduced output from MasterMacsDriver 2023-07-13 10:57:07 +02:00
f1a17bc295 Added AMOR detector tower special pmac motors: working now 2023-07-07 13:54:21 +02:00
8a6441927a Implemented that enable commands are only sent when a status chnage is required
Stop commands are only sent by EL734 and pmac when actually moving. The
motor record logic calls stop() in an excessive way.
2023-05-31 09:37:22 +02:00
72afd02b4e Many improvements to the MasterMACS motor driver. It is working but the hardware is shaky.
Added support for dose rate controlled Phytron motors. Not tested!
Small bug fixes
2023-05-31 09:13:41 +02:00
f333a27482 Added missing file after merging MasterMACS 2023-03-21 15:05:25 +01:00
4a2731b054 Added yet another missing file 2023-03-21 14:58:21 +01:00
9055a86b57 Added two missing files 2023-03-21 14:56:38 +01:00
ccd73babd5 First working version of the MasterMACS EPICS driver
Also added some test code
2023-03-21 14:55:07 +01:00
b8896b7a85 Implemented speed for the PMAC controller 2023-02-22 11:54:54 +01:00
acf1751081 Merge branch 'master' into speed 2023-02-17 10:46:06 +01:00
61290b5e2c Added utility programs which update motor subsitutions files from
the values read rom the actual controller
2023-02-17 10:45:15 +01:00
d706915a46 Implemented changing speed on EL734
Added a test IOCs for EL734
2023-02-17 10:43:51 +01:00
3c9932dc18 - Reduced poll rate of EL737
- Removed unneeded hardware limit reading code from EL734
2023-02-16 14:29:05 +01:00
9b9072b83b Improved error handling in the EL737 scalerRecord driver 2023-01-31 11:42:04 +01:00
3b2a21094c Fixed pmacV3 axis codes 2023-01-12 15:16:49 +01:00
a899a28182 sMore debugging output in devScalerEL737 2023-01-11 15:09:43 +01:00
73 changed files with 24903 additions and 856 deletions

246
.clang-format Normal file
View 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
View File

@ -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

View File

@ -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

View File

@ -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
View 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

View File

@ -38,3 +38,12 @@ Those political problems require a special development model:
Take care of the sinqEPICsApp/src/sinq.dbd file. This is the one which differs mostly between
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>
```

View 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)")

View 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")
}

View 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";}
}

View 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")
# }

View 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)
}

View 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")
}

View 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)")

View 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)")

View 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);

View File

@ -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);
}

View File

@ -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();
};

View File

@ -279,6 +279,7 @@ EuroMoveAxis::EuroMoveAxis(EuroMoveController *pC, int axisNo)
pC_(pC)
{
motNo = axisNo;
next_poll = -1;
}

View File

@ -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

View 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);
}

View 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_;
};

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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

View File

@ -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.....
};

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

View File

@ -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"

View 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")
}

View 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")
}

View 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")
}

View 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
View 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
View 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
View 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
View 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")

View File

@ -0,0 +1,9 @@
record(asyn,"$(P)$(R)")
{
field(DTYP,"asynRecordDevice")
field(PORT,"$(PORT)")
field(ADDR,"$(ADDR)")
field(OMAX,"$(OMAX)")
field(IMAX,"$(IMAX)")
}

View 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")
}

View 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")
}

View 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
View 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")
}

View 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
View 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","")

View 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}
}

View File

@ -0,0 +1,9 @@
record(asyn,"$(P)$(R)")
{
field(DTYP,"asynRecordDevice")
field(PORT,"$(PORT)")
field(ADDR,"$(ADDR)")
field(OMAX,"$(OMAX)")
field(IMAX,"$(IMAX)")
}

View 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")
}

View 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")
}

View 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
View 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")
}

View 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
View 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
View 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
View 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
View 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
View 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, "")

View File

@ -0,0 +1,9 @@
record(asyn,"$(P)$(R)")
{
field(DTYP,"asynRecordDevice")
field(PORT,"$(PORT)")
field(ADDR,"$(ADDR)")
field(OMAX,"$(OMAX)")
field(IMAX,"$(IMAX)")
}

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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")
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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()