14 Commits
0.1.0 ... 0.6.0

Author SHA1 Message Date
699b588ba5 Replaced ipPortUser_ with ipPortAsynOctetSyncIO_
See comment to sinqMotor 0.12.0
2025-04-15 17:22:15 +02:00
e86c517fc7 Bumped required sinqMotor version to 0.11.0 2025-04-10 09:09:27 +02:00
f733718ee7 Using appropriate sinqMotor version 2025-04-09 15:26:45 +02:00
a8c3499dc5 Set required sinqMotor version to mathis_s 2025-04-04 13:31:49 +02:00
fe52245e38 Custom timeout for enable and position methods
Added a custom timeout for the enable command, as it takes quite a bit
of time for the motor controller to answer and we don't want to show a
premature communication timeout error. Also changed the code in order to
use the motorPosition() and setMotorPosition() methods instead of
directly accessing the paramLib.
2025-03-31 10:48:41 +02:00
a3e849f386 Changed to the "motorPosition" and "setMotorPosition" functions provided
by sinqMotor.
2025-03-28 14:53:04 +01:00
16564011a6 Added stop and error reset function for masterMacs 2025-03-19 15:07:09 +01:00
631ee46a50 Removed friend class declaration and replaced access to private,properties with accessors 2025-03-10 17:07:33 +01:00
cf9899062a Modified communication protocol for MCU software 2.0 2025-03-10 14:29:56 +01:00
d975af75a2 Suppress strcpy-related errors on a global level 2025-03-04 14:45:47 +01:00
55aabb0468 Suppress strcpy-linter message 2025-03-04 14:37:04 +01:00
755ae42b3b Added cppcheck-suppress for strcpy - I know strcpy is safe because I
check the length beforehand.
2025-03-04 14:19:19 +01:00
4ad842c097 Added new feature msgPrintControl from sinqMotor 0.8.0.
Correspondingly, the minimum version requirement for sinqMotor has been bumped to 0.8.0.
2025-03-04 12:42:11 +01:00
cb91a8aa36 Added .clang-format file 2025-02-17 09:31:35 +01:00
10 changed files with 773 additions and 628 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
---

View File

@ -9,7 +9,7 @@ stages:
cppcheck:
stage: lint
script:
- cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
- cppcheck --std=c++17 --addon=cert --addon=misc --suppress=cert-STR07-C --error-exitcode=1 src/*.cpp
artifacts:
expire_in: 1 week
tags:
@ -24,16 +24,6 @@ formatting:
tags:
- sinq
# clangtidy:
# stage: lint
# script:
# - curl https://docker.psi.ch:5000/v2/_catalog
# # - dnf update -y
# # - dnf install -y clang-tools-extra
# # - clang-tidy sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h -checks=cppcoreguidelines-*,cert-*
# # tags:
# # - sinq
build_module:
stage: build
script:

View File

@ -13,7 +13,7 @@ REQUIRED+=sinqMotor
motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=0.7.0
sinqMotor_VERSION=0.12.0
# These headers allow to depend on this library for derived drivers.
HEADERS += src/masterMacsAxis.h

View File

@ -1,9 +1,13 @@
# masterMacs
## <span style="color:red">Please read the documentation of sinqMotor first: https://git.psi.ch/sinq-epics-modules/sinqmotor</span>
## Overview
This is a driver for the masterMacs motion controller with the SINQ communication protocol. It is based on the sinqMotor shared library (https://git.psi.ch/sinq-epics-modules/sinqmotor). The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
Compatible to MasterMACS software 2.0.0.
## User guide
This driver is a standard sinqMotor-derived driver and does not need any specific configuration. For the general configuration, please see https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
@ -18,22 +22,42 @@ The folder "utils" contains utility scripts for working with masterMacs motor co
masterMacs exposes the following IOC shell functions (all in masterMacsController.cpp):
- `masterMacsController`: Create a new controller object.
- `masterMacsAxis`: Create a new axis object.
These functions are parametrized as follows:
The full mcu.cmd file looks like this:
```
masterMacsController(
"$(NAME)", # Name of the MCU, e.g. mcu1. This parameter should be provided by an environment variable.
"$(ASYN_PORT)", # IP-Port of the MCU. This parameter should be provided by an environment variable.
8, # Maximum number of axes
0.05, # Busy poll period in seconds
1, # Idle poll period in seconds
0.05 # Communication timeout in seconds
);
```
```
masterMacsAxis(
"$(NAME)", # Name of the associated MCU, e.g. mcu1. This parameter should be provided by an environment variable.
1, # Index of the axis.
);
# Define the name of the controller and the corresponding port
epicsEnvSet("NAME","mcu")
epicsEnvSet("ASYN_PORT","p$(NAME)")
# Create the TCP/IP socket used to talk with the controller. The socket can be adressed from within the IOC shell via the port name
drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
# Create the controller object with the defined name and connect it to the socket via the port name.
# The other parameters are as follows:
# 8: Maximum number of axes
# 0.05: Busy poll period in seconds
# 1: Idle poll period in seconds
# 1: Socket communication timeout in seconds
masterMacsController("$(NAME)", "$(ASYN_PORT)", 8, 0.05, 1, 1);
# Define some axes for the specified MCU at the given slot (1, 2 and 5). No slot may be used twice!
masterMacsAxis("$(NAME)",1);
masterMacsAxis("$(NAME)",2);
masterMacsAxis("$(NAME)",5);
# Set the number of subsequent timeouts
setMaxSubsequentTimeouts("$(NAME)", 20);
# Configure the timeout frequency watchdog:
setThresholdComTimeout("$(NAME)", 100, 1);
# Parametrize the EPICS record database with the substitution file named after the MCU.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(masterMacs_DB)/masterMacs.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
```
### Versioning

View File

@ -34,6 +34,34 @@ static void epicsInithookFunction(initHookState iState) {
}
}
void appendErrorMessage(char *fullMessage, size_t capacityFullMessage,
const char *toBeAppended) {
size_t lenFullMessage = strlen(fullMessage);
size_t lenToBeAppended = strlen(toBeAppended);
if (lenFullMessage == 0) {
// The error message is empty -> Just copy the content of toBeAppended
// into fullMessage, if the formers capacity suffices
if (lenToBeAppended < capacityFullMessage) {
// We check before that the capacity of fullMessage is sufficient
strcpy(fullMessage, toBeAppended);
}
} else {
// Append the message and add a linebreak in between, if the capacity of
// fullMessage suffices. We need capacity for one additional character
// because of the linebreak.
if (lenFullMessage + lenToBeAppended + 1 < capacityFullMessage) {
// Append the linebreak and readd the null terminator behind it
// fullMessage[lenFullMessage] = '\n';
// fullMessage[lenFullMessage + 1] = '\0';
// We check before that the capacity of fullMessage is sufficient
strcat(fullMessage, toBeAppended);
}
}
}
masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
: sinqAxis(pC, axisNo), pC_(pC) {
@ -50,13 +78,13 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
bounds, asynMotorAxis prints an error. However, we want the IOC creation to
stop completely, since this is a configuration error.
*/
if (axisNo >= pC->numAxes_) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
if (axisNo >= pC->numAxes()) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:: FATAL ERROR: "
"Axis index %d must be smaller than the total number of axes "
"%d. Call the support.",
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, axisNo_,
pC->numAxes_);
pC->numAxes());
exit(-1);
}
@ -80,10 +108,10 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
timeAtHandshake_ = 0;
// masterMacs motors can always be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 1);
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
@ -92,10 +120,10 @@ masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
}
// Assume that the motor is initially not moving
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving_, false);
status = pC_->setIntegerParam(axisNo_, pC_->motorStatusMoving(), false);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
"(setting a parameter value failed with %s)\n. Terminating IOC",
pC_->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
@ -135,11 +163,11 @@ asynStatus masterMacsAxis::init() {
time_t now = time(NULL);
time_t maxInitTime = 60;
while (1) {
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status == asynParamUndefined) {
if (now + maxInitTime < time(NULL)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nInitializing the parameter library failed.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
@ -162,7 +190,7 @@ asynStatus masterMacsAxis::init() {
}
nvals = sscanf(response, "%lf", &motorPosition);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R12", response, axisNo_,
return pC_->couldNotParseResponse("R12", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -173,7 +201,7 @@ asynStatus masterMacsAxis::init() {
}
nvals = sscanf(response, "%lf", &motorVelocity);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R05", response, axisNo_,
return pC_->couldNotParseResponse("R05", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -184,7 +212,7 @@ asynStatus masterMacsAxis::init() {
}
nvals = sscanf(response, "%lf", &motorVmax);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R26", response, axisNo_,
return pC_->couldNotParseResponse("R26", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -195,7 +223,7 @@ asynStatus masterMacsAxis::init() {
}
nvals = sscanf(response, "%lf", &motorAccel);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R06", response, axisNo_,
return pC_->couldNotParseResponse("R06", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -204,15 +232,10 @@ asynStatus masterMacsAxis::init() {
// MasterMACS.
lastSetSpeed_ = motorVelocity;
// Transform from motor to parameter library coordinates
motorPosition = motorPosition / motorRecResolution;
// Store these values in the parameter library
pl_status =
pC_->setDoubleParam(axisNo_, pC_->motorPosition_, motorPosition);
// Store the motor position in the parameter library
pl_status = setMotorPosition(motorPosition);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
return pl_status;
}
// Write to the motor record fields
@ -236,7 +259,7 @@ asynStatus masterMacsAxis::init() {
// If we can't communicate with the parameter library, it doesn't
// make sense to try and upstream this to the user -> Just log the
// error
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\ncallParamCallbacks failed with %s.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -275,6 +298,34 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Are we currently waiting for a handshake?
if (waitForHandshake_) {
// Check if the handshake takes too long and communicate an error in
// this case. A handshake should not take more than 5 seconds.
time_t currentTime = time(NULL);
bool timedOut = (currentTime > timeAtHandshake_ + 5);
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut,
pC_->asynUserSelf())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAsked for a "
"handshake at %ld s and didn't get a positive reply yet "
"(current time is %ld s).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
timeAtHandshake_, currentTime);
}
if (timedOut) {
pl_status =
setStringParam(pC_->motorMessageText(),
"Timed out while waiting for a handshake");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
pC_->read(axisNo_, 86, response);
if (rw_status != asynSuccess) {
return rw_status;
@ -282,8 +333,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
nvals = sscanf(response, "%lf", &handshakePerformed);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R86", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R86", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
if (handshakePerformed == 1.0) {
@ -295,14 +346,14 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// poll. This is already part of the movement procedure.
*moving = true;
pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving);
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusMoving_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDone_, !(*moving));
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_",
axisNo_, __PRETTY_FUNCTION__,
@ -314,7 +365,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
// Motor resolution from parameter library
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
@ -323,25 +374,20 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
// Read the previous motor position
pl_status =
pC_->getDoubleParam(axisNo_, pC_->motorPosition_, &previousPosition);
pl_status = motorPosition(&previousPosition);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
return pl_status;
}
// Transform from EPICS to motor coordinates (see comment in
// masterMacsAxis::readConfig())
previousPosition = previousPosition * motorRecResolution;
// Update the axis status
rw_status = readAxisStatus();
if (rw_status != asynSuccess) {
return rw_status;
}
// Check if we reached the target
if (targetReached()) {
// If the motor is switched off or if it reached its target, it is not
// moving.
if (targetReached() || !switchedOn()) {
*moving = false;
} else {
*moving = true;
@ -354,7 +400,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
nvals = sscanf(response, "%lf", &currentPosition);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse("R12", response, axisNo_,
return pC_->couldNotParseResponse("R12", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -362,73 +408,29 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
Read out the error if either a fault condition status flag has been set or
if a movement has just ended.
*/
if (faultConditionSet()) {
msgPrintControlKey keyError = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (faultConditionSet() || !(*moving)) {
rw_status = readAxisError();
if (shortCircuit()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nShort "
"circuit fault.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText_,
"Short circuit error. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
if (encoderError()) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nEncoder error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText_,
"Encoder error. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
if (followingError()) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed "
"following error exceeded.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status = setStringParam(
pC_->motorMessageText_,
"Maximum allowed following error exceeded.Check if movement "
"range is blocked. Otherwise please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
/*
A communication error is a special case. If a communication between
controller and axis error occurred, all subsequent errors are ignored,
since this information is not reliable.
*/
if (communicationError()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
if (pC_->getMsgPrintControl().shouldBePrinted(
keyError, true, pC_->asynUserSelf())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nCommunication error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
"%d\nCommunication error.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->getMsgPrintControl().getSuffix());
}
pl_status =
setStringParam(pC_->motorMessageText_,
setStringParam(pC_->motorMessageText(),
"Communication error between PC and motor "
"controller. Please call the support.");
if (pl_status != asynSuccess) {
@ -437,173 +439,165 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
__LINE__);
}
poll_status = asynError;
} else {
// This buffer must be initialized to zero because we build the
// error message by appending strings.
char userMessage[pC_->MAXBUF_] = {0};
char shellMessage[pC_->MAXBUF_] = {0};
// Concatenate all other errors
if (shortCircuit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Short circuit fault.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Short circuit error. Please call the support.");
poll_status = asynError;
}
if (encoderError()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Encoder error.");
appendErrorMessage(userMessage, sizeof(userMessage),
"Encoder error. Please call the support.");
poll_status = asynError;
}
if (followingError()) {
appendErrorMessage(
shellMessage, sizeof(shellMessage),
"Maximum callowed following error exceeded.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Maximum allowed following error exceeded.Check if "
"movement range is blocked. Otherwise please call the "
"support.");
poll_status = asynError;
}
if (feedbackError()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nFeedback error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText_,
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Feedback error.");
appendErrorMessage(userMessage, sizeof(userMessage),
"Feedback error. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
/*
Either the software limits or the end switches of the controller have
been hit. Since the EPICS limits are derived from the software limits
and are a little bit smaller, these error cases can only happen if
either the axis has an incremental encoder which is not properly homed
or if a bug occured.
Either the software limits or the end switches of the controller
have been hit. Since the EPICS limits are derived from the software
limits and are a little bit smaller, these error cases can only
happen if either the axis has an incremental encoder which is not
properly homed or if a bug occured.
*/
if (positiveLimitSwitch() || negativeLimitSwitch() ||
positiveSoftwareLimit() || negativeSoftwareLimit()) {
// Distinction for developers
if (positiveLimitSwitch()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis "
"hit the positive limit end switch.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
__LINE__);
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Positive limit switch.");
}
if (negativeLimitSwitch()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis "
"hit the negative limit end switch.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
__LINE__);
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Negative limit switch.");
}
if (positiveSoftwareLimit()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis "
"hit the positive software limit.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
__LINE__);
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Positive software limit.");
}
if (negativeSoftwareLimit()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis "
"hit the negative software limit.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
__LINE__);
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Negative software limit.");
}
// Generic error message for user
pl_status = setStringParam(
pC_->motorMessageText_,
appendErrorMessage(
userMessage, sizeof(userMessage),
"Software limits or end switch hit. Try homing the motor, "
"moving in the opposite direction or check the SPS for "
"errors (if available). Otherwise please call the "
"support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
if (overCurrent()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nOvercurrent "
"error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText_,
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overcurrent error.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Overcurrent error. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
if (overTemperature()) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nOvertemperature "
"error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status = setStringParam(
pC_->motorMessageText_,
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overtemperature error.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Overtemperature error. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
if (overVoltage()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nOvervoltage "
"error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText_,
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overvoltage error.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Overvoltage error. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
if (underVoltage()) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUndervoltage "
"error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText_,
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Undervoltage error.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Undervoltage error. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
if (stoFault()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nSTO fault - "
"STO input is on disable state.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status = setStringParam(pC_->motorMessageText_,
appendErrorMessage(shellMessage, sizeof(shellMessage),
"STO input is on disable state.");
appendErrorMessage(userMessage, sizeof(userMessage),
"STO fault. Please call the support.");
poll_status = asynError;
}
if (strlen(shellMessage) > 0) {
if (pC_->getMsgPrintControl().shouldBePrinted(
keyError, true, pC_->asynUserSelf())) {
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\n%s%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
__LINE__, shellMessage,
pC_->getMsgPrintControl().getSuffix());
}
}
pl_status = setStringParam(pC_->motorMessageText(), userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
}
} else {
pC_->getMsgPrintControl().resetCount(keyError, pC_->asynUserSelf());
}
// Read out the limits, if the motor is not moving
@ -614,8 +608,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
nvals = sscanf(response, "%lf", &lowLimit);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R34", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R34", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
rw_status = pC_->read(axisNo_, 33, response);
@ -624,8 +618,8 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
nvals = sscanf(response, "%lf", &highLimit);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R33", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R33", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
/*
@ -641,7 +635,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
directly, but need to shrink them a bit. In this case, we're shrinking
them by 0.1 mm or 0.1 degree (depending on the axis type) on both sides.
*/
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset_,
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset(),
&limitsOffset);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLimitsOffset_",
@ -651,15 +645,15 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
highLimit = highLimit - limitsOffset;
lowLimit = lowLimit + limitsOffset;
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver_,
highLimit);
pl_status = pC_->setDoubleParam(
axisNo_, pC_->motorHighLimitFromDriver(), highLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(
pl_status, "motorHighLimitFromDriver_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver_,
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
lowLimit);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_",
@ -669,7 +663,7 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
// Update the enable PV
pl_status = setIntegerParam(pC_->motorEnableRBV_,
pl_status = setIntegerParam(pC_->motorEnableRBV(),
readyToBeSwitchedOn() && switchedOn());
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorEnableRBV_", axisNo_,
@ -687,41 +681,36 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
// Update the parameter library
if (hasError) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
pl_status = setIntegerParam(pC_->motorStatusMoving_, *moving);
pl_status = setIntegerParam(pC_->motorStatusMoving(), *moving);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusMoving_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDone_, !(*moving));
pl_status = setIntegerParam(pC_->motorStatusDone(), !(*moving));
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDone_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = setIntegerParam(pC_->motorStatusDirection_, direction);
pl_status = setIntegerParam(pC_->motorStatusDirection(), direction);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusDirection_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
// Transform from motor to EPICS coordinates (see comment in
// masterMacsAxis::init())
currentPosition = currentPosition / motorRecResolution;
pl_status = setDoubleParam(pC_->motorPosition_, currentPosition);
pl_status = setMotorPosition(currentPosition);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorPosition_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
return pl_status;
}
return poll_status;
@ -746,13 +735,13 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// =========================================================================
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV_, &enabled);
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorEnableRBV(), &enabled);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "enableMotorRBV_", axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_,
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorRecResolution(),
&motorRecResolution);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorRecResolution_",
@ -761,7 +750,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
}
if (enabled == 0) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is "
"disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
@ -773,12 +762,12 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
motorVelocity = maxVelocity * motorRecResolution;
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
pC_->asynUserSelf(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\nMove to position %lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
// Check if the speed is allowed to be changed
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed_,
pl_status = pC_->getIntegerParam(axisNo_, pC_->motorCanSetSpeed(),
&motorCanSetSpeed);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorCanSetSpeed_",
@ -789,7 +778,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
// Initialize the movement handshake
rw_status = pC_->write(axisNo_, 86, "0");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
@ -807,7 +796,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
snprintf(value, sizeof(value), "%lf", motorVelocity);
rw_status = pC_->write(axisNo_, 05, value);
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
@ -816,7 +805,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
return rw_status;
}
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\nSetting speed "
"to %lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -827,7 +816,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
snprintf(value, sizeof(value), "%lf", motorCoordinatesPosition);
rw_status = pC_->write(axisNo_, 02, value);
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
@ -843,7 +832,7 @@ asynStatus masterMacsAxis::doMove(double position, int relative,
rw_status = pC_->write(axisNo_, 00, "1");
}
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
@ -878,7 +867,29 @@ asynStatus masterMacsAxis::stop(double acceleration) {
rw_status = pC_->write(axisNo_, 00, "8");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
return rw_status;
}
asynStatus masterMacsAxis::doReset() {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
// =========================================================================
rw_status = pC_->write(axisNo_, 17, "");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_",
axisNo_, __PRETTY_FUNCTION__,
@ -905,7 +916,7 @@ asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
// =========================================================================
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType_,
pl_status = pC_->getStringParam(axisNo_, pC_->encoderType(),
sizeof(response), response);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "encoderType_", axisNo_,
@ -918,7 +929,7 @@ asynStatus masterMacsAxis::doHome(double min_velocity, double max_velocity,
// Initialize the movement handshake
rw_status = pC_->write(axisNo_, 86, "0");
if (rw_status != asynSuccess) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);
pl_status = setIntegerParam(pC_->motorStatusProblem(), true);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status,
"motorStatusProblem_", axisNo_,
@ -967,7 +978,7 @@ asynStatus masterMacsAxis::readEncoderType() {
nvals = sscanf(response, "%d", &encoder_id);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(command, response, axisNo_,
return pC_->couldNotParseResponse(command, response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@ -978,9 +989,9 @@ asynStatus masterMacsAxis::readEncoderType() {
2=SSI (Absolute encoder with BiSS interface)
*/
if (encoder_id == 0) {
pl_status = setStringParam(pC_->encoderType_, IncrementalEncoder);
pl_status = setStringParam(pC_->encoderType(), IncrementalEncoder);
} else {
pl_status = setStringParam(pC_->encoderType_, AbsoluteEncoder);
pl_status = setStringParam(pC_->encoderType(), AbsoluteEncoder);
}
if (pl_status != asynSuccess) {
@ -1012,13 +1023,13 @@ asynStatus masterMacsAxis::enable(bool on) {
// axis instead of "moving", since status -6 is also moving, but the
// motor can actually be disabled in this state!
if (moving) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not "
"idle and can therefore not be disabled.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status =
setStringParam(pC_->motorMessageText_,
setStringParam(pC_->motorMessageText(),
"Axis cannot be disabled while it is moving.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
@ -1034,7 +1045,7 @@ asynStatus masterMacsAxis::enable(bool on) {
if ((readyToBeSwitchedOn() && switchedOn()) == on) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_WARNING,
pC_->asynUserSelf(), ASYN_TRACE_WARNING,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is already %s.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
on ? "enabled" : "disabled");
@ -1043,21 +1054,25 @@ asynStatus masterMacsAxis::enable(bool on) {
// Enable / disable the axis if it is not moving
snprintf(value, sizeof(value), "%d", on);
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d:\n%s axis.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
on ? "Enable" : "Disable");
if (on == 0) {
pl_status = setStringParam(pC_->motorMessageText_, "Disabling ...");
pl_status = setStringParam(pC_->motorMessageText(), "Disabling ...");
} else {
pl_status = setStringParam(pC_->motorMessageText_, "Enabling ...");
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
}
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
rw_status = pC_->write(axisNo_, 04, value);
// The answer to the enable command on MasterMACS might take some time,
// hence we wait for a custom timespan in seconds instead of
// pC_->comTimeout_
rw_status = pC_->write(axisNo_, 04, value, 1.0);
if (rw_status != asynSuccess) {
return rw_status;
}
@ -1084,7 +1099,7 @@ asynStatus masterMacsAxis::enable(bool on) {
// Failed to change axis status within timeout_enable_disable => Send a
// corresponding message
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
asynPrint(pC_->asynUserSelf(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFailed to %s axis "
"within %d seconds\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
@ -1093,7 +1108,7 @@ asynStatus masterMacsAxis::enable(bool on) {
// Output message to user
snprintf(value, sizeof(value), "Failed to %s within %d seconds",
on ? "enable" : "disable", timeout_enable_disable);
pl_status = setStringParam(pC_->motorMessageText_, "Enabling ...");
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
@ -1127,8 +1142,8 @@ asynStatus masterMacsAxis::readAxisStatus() {
float axisStatus = 0;
int nvals = sscanf(response, "%f", &axisStatus);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R10", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R10", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
axisStatus_ = toBitset(axisStatus);
@ -1148,8 +1163,8 @@ asynStatus masterMacsAxis::readAxisError() {
float axisError = 0;
int nvals = sscanf(response, "%f", &axisError);
if (nvals != 1) {
return pC_->errMsgCouldNotParseResponse(
"R11", response, axisNo_, __PRETTY_FUNCTION__, __LINE__);
return pC_->couldNotParseResponse("R11", response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
axisError_ = toBitset(axisError);
}

View File

@ -4,7 +4,7 @@
#include <bitset>
// Forward declaration of the controller class to resolve the cyclic dependency
// between C804Controller.h and C804Axis.h. See
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class masterMacsController;
@ -24,15 +24,6 @@ class masterMacsAxis : public sinqAxis {
*/
virtual ~masterMacsAxis();
/**
* @brief Implementation of the `stop` function from asynMotorAxis
*
* @param acceleration Acceleration ACCEL from the motor record. This
* value is currently not used.
* @return asynStatus
*/
asynStatus stop(double acceleration);
/**
* @brief Implementation of the `doHome` function from sinqAxis. The
* parameters are described in the documentation of `sinqAxis::doHome`.
@ -69,6 +60,23 @@ class masterMacsAxis : public sinqAxis {
asynStatus doMove(double position, int relative, double min_velocity,
double max_velocity, double acceleration);
/**
* @brief Implementation of the `stop` function from asynMotorAxis
*
* @param acceleration Acceleration ACCEL from the motor record. This
* value is currently not used.
* @return asynStatus
*/
asynStatus stop(double acceleration);
/**
* @brief Implementation of the `doReset` function from sinqAxis.
*
* @param on
* @return asynStatus
*/
asynStatus doReset();
/**
* @brief Readout of some values from the controller at IOC startup
*
@ -273,9 +281,6 @@ class masterMacsAxis : public sinqAxis {
* @brief Read the property from axisError_
*/
bool stoFault() { return axisError_[15]; }
private:
friend class masterMacsController;
};
#endif

View File

@ -49,14 +49,10 @@ masterMacsController::masterMacsController(const char *portName,
int numAxes, double movingPollPeriod,
double idlePollPeriod,
double comTimeout)
: sinqController(
portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod,
/*
The following parameter library entries are added in this driver:
- REREAD_ENCODER_POSITION
- READ_CONFIG
*/
NUM_masterMacs_DRIVER_PARAMS)
: sinqController(portName, ipPortConfigName, numAxes, movingPollPeriod,
idlePollPeriod,
// No additional parameter library entries
0)
{
@ -64,39 +60,26 @@ masterMacsController::masterMacsController(const char *portName,
asynStatus status = asynSuccess;
// Initialization of all member variables
lowLevelPortUser_ = nullptr;
comTimeout_ = comTimeout;
// =========================================================================;
/*
We try to connect to the port via the port name provided by the constructor.
If this fails, the function is terminated via exit
*/
pasynOctetSyncIO->connect(ipPortConfigName, 0, &lowLevelPortUser_, NULL);
if (status != asynSuccess || lowLevelPortUser_ == nullptr) {
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
"connect to MCU controller).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__);
exit(-1);
}
// =========================================================================
/*
Define the end-of-string of a message coming from the device to EPICS.
It is not necessary to append a terminator to outgoing messages, since
the message length is encoded in the message header in the getSetResponse
method.
the message length is encoded in the message header.
*/
const char *message_from_device = "\x03"; // Hex-code for ETX
status = pasynOctetSyncIO->setInputEos(
lowLevelPortUser_, message_from_device, strlen(message_from_device));
const char *message_from_device = "\x0D"; // Hex-code for CR
status = pasynOctetSyncIO->setInputEos(ipPortAsynOctetSyncIO_,
message_from_device,
strlen(message_from_device));
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (setting "
"input EOS failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
pasynOctetSyncIO->disconnect(ipPortAsynOctetSyncIO_);
exit(-1);
}
@ -107,7 +90,7 @@ masterMacsController::masterMacsController(const char *portName,
"ParamLib callbacks failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
pasynOctetSyncIO->disconnect(ipPortAsynOctetSyncIO_);
exit(-1);
}
}
@ -117,53 +100,33 @@ Access one of the axes of the controller via the axis adress stored in asynUser.
If the axis does not exist or is not a Axis, a nullptr is returned and an
error is emitted.
*/
masterMacsAxis *masterMacsController::getAxis(asynUser *pasynUser) {
masterMacsAxis *masterMacsController::getMasterMacsAxis(asynUser *pasynUser) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(pasynUser);
return masterMacsController::castToAxis(asynAxis);
return dynamic_cast<masterMacsAxis *>(asynAxis);
}
/*
Access one of the axes of the controller via the axis index.
If the axis does not exist or is not a Axis, the function must return Null
*/
masterMacsAxis *masterMacsController::getAxis(int axisNo) {
masterMacsAxis *masterMacsController::getMasterMacsAxis(int axisNo) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(axisNo);
return masterMacsController::castToAxis(asynAxis);
return dynamic_cast<masterMacsAxis *>(asynAxis);
}
masterMacsAxis *masterMacsController::castToAxis(asynMotorAxis *asynAxis) {
// =========================================================================
// If the axis slot of the pAxes_ array is empty, a nullptr must be returned
if (asynAxis == nullptr) {
return nullptr;
}
// Here, an error is emitted since asyn_axis is not a nullptr but also not
// an instance of Axis
masterMacsAxis *axis = dynamic_cast<masterMacsAxis *>(asynAxis);
if (axis == nullptr) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nAxis is not "
"an instance of masterMacsAxis",
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
}
return axis;
}
asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response) {
asynStatus masterMacsController::read(int axisNo, int tcpCmd, char *response,
double comTimeout) {
return writeRead(axisNo, tcpCmd, NULL, response);
}
asynStatus masterMacsController::write(int axisNo, int tcpCmd,
const char *payload) {
return writeRead(axisNo, tcpCmd, payload, NULL);
const char *payload, double comTimeout) {
return writeRead(axisNo, tcpCmd, payload, NULL, comTimeout);
}
asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
const char *payload,
char *response) {
const char *payload, char *response,
double comTimeout) {
// Definition of local variables.
asynStatus status = asynSuccess;
@ -195,94 +158,94 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// =========================================================================
masterMacsAxis *axis = getAxis(axisNo);
// Check if a timeout has been given
if (comTimeout < 0.0) {
comTimeout = comTimeout_;
}
masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
}
/*
PSI SINQ uses a custom protocol which is described in
PSI_TCP_Interface_V1-8.pdf (p. // 4-17).
A special case is the message length, which is specified by two bytes
LSB and MSB: MSB = message length / 256 LSB = message length % 256. For
example, a message length of 47 chars would result in MSB = 0, LSB = 47,
whereas a message length of 356 would result in MSB = 1, LSB = 100.
The full protocol looks as follows:
0x05 -> Start of protocol frame ENQ
[LSB]
[MSB]
0x19 -> Data type PDO1
value [Actual message] It is not necessary to append a terminator, since
this protocol encodes the message length in LSB and MSB.
0x0D -> Carriage return (ASCII alias \r)
0x03 -> End of text ETX
*/
fullCommand[0] = '\x05'; // ENQ
fullCommand[1] = 1; // Placeholder value, can be anything other than 0
fullCommand[2] = 1; // Placeholder value, can be anything other than 0
fullCommand[3] = '\x19'; // PD01
// Create the command and add CR and ETX at the end
if (isRead) {
snprintf(&fullCommand[4], MAXBUF_ - 4, "%dR%02d\x0D\x03", axisNo,
tcpCmd);
snprintf(fullCommand, MAXBUF_ - 1, "%dR%02d\x0D", axisNo, tcpCmd);
} else {
snprintf(&fullCommand[4], MAXBUF_ - 4, "%dS%02d=%s\x0D\x03", axisNo,
tcpCmd, payload);
if (strlen(payload) == 0) {
snprintf(fullCommand, MAXBUF_ - 1, "%dS%02d\x0D", axisNo, tcpCmd);
} else {
snprintf(fullCommand, MAXBUF_ - 1, "%dS%02d=%s\x0D", axisNo, tcpCmd,
payload);
}
}
// Calculate the command length
const size_t fullCommandLength = strlen(fullCommand);
// Length of the command without ENQ and ETX
const size_t lenWithMetadata = fullCommandLength - 2;
// Perform both division and modulo operation at once.
div_t lenWithMetadataSep = std::div(lenWithMetadata, 256);
// Now set the actual command length
fullCommand[1] = lenWithMetadataSep.rem; // LSB
fullCommand[2] = lenWithMetadataSep.quot; // MSB
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
asynPrint(
this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line %d:\nSending command %s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, printableCommand);
// Send out the command
status =
pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
fullCommandLength, comTimeout_, &nbytesOut);
status = pasynOctetSyncIO->write(ipPortAsynOctetSyncIO_, fullCommand,
fullCommandLength, comTimeout, &nbytesOut);
if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nError "
"%s while writing to the controller\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
}
msgPrintControlKey writeKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynSuccess) {
msgPrintControl_.resetCount(writeKey, pasynUserSelf);
// Try to read the answer repeatedly
int maxTrials = 2;
for (int i = 0; i < maxTrials; i++) {
status =
pasynOctetSyncIO->read(lowLevelPortUser_, fullResponse, MAXBUF_,
comTimeout_, &nbytesIn, &eomReason);
/*
A typical response of the MasterMacs controller looks like this:
(.. TCP Header ...) 31 20 52 20 31 31 3d 35 31 32 2e 30 30 30 30 06
0d 00 00 00 00 00 00 00 00 00 00 00 00 00
The message terminator is the carriage return (0d), which is
specified in the controller constructor as the end-of-string (EOS)
character. However, we also need to remove the buffer zeros at the
end, because they will otherwise confuse the
pasynOctetSyncIO->read() during the next call. The
pasynOctetSyncIO->flush() method does exactly that: it takes all
bytes it can find in the socket and throws them away. We don't check
the return value of flush(), because it is always asynSuccess (see
https://www.slac.stanford.edu/grp/lcls/controls/global/doc/epics-modules/R3-14-12/asyn/asyn-R4-18-lcls2/asyn/interfaces/asynOctetBase.c)
*/
status = pasynOctetSyncIO->read(ipPortAsynOctetSyncIO_,
fullResponse, MAXBUF_, comTimeout,
&nbytesIn, &eomReason);
pasynOctetSyncIO->flush(ipPortAsynOctetSyncIO_);
if (status == asynSuccess) {
status = parseResponse(fullCommand, fullResponse,
drvMessageText, &valueStart, &valueStop,
axisNo, tcpCmd, isRead);
if (status == asynSuccess) {
// Received the correct message
break;
}
} else {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
if (status != asynTimeout) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nError "
"%s while reading from the controller\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
break;
}
}
if (i + 1 == maxTrials && status == asynError) {
asynPrint(
@ -294,16 +257,15 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
}
} else {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nError %s while "
"writing to the controller\n",
"writing to the controller.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
}
}
// MasterMACS needs a bit of time between messages, therefore thr program
// execution is paused after the communication happened.
// usleep(1500);
// Create custom error messages for different failure modes
switch (status) {
@ -345,10 +307,6 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Log the overall status (communication successfull or not)
if (status == asynSuccess) {
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
asynPrint(
lowLevelPortUser_, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line %d:\nReturn value: %s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, printableResponse);
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
} else {
@ -408,6 +366,9 @@ asynStatus masterMacsController::parseResponse(
char printableCommand[MAXBUF_] = {0};
char printableResponse[MAXBUF_] = {0};
msgPrintControlKey parseKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
// We don't use strlen here since the C string terminator 0x00
// occurs in the middle of the char array.
for (uint32_t i = 0; i < MAXBUF_; i++) {
@ -422,27 +383,39 @@ asynStatus masterMacsController::parseResponse(
} else if (fullResponse[i] == '\x15') {
// NAK
snprintf(drvMessageText, MAXBUF_, "Communication failed.");
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nCommunication failed\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
"%d:\nCommunication failed.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
msgPrintControl_.getSuffix());
}
break;
} else if (fullResponse[i] == '\x18') {
// CAN
snprintf(drvMessageText, MAXBUF_,
"Tried to write with a read-only command. This is a "
"bug, please call the support.");
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nTried to "
"write with the read-only command %s\n",
"write with the read-only command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableCommand);
printableCommand, msgPrintControl_.getSuffix());
}
responseValid = false;
break;
}
}
if (responseValid) {
msgPrintControl_.resetCount(parseKey, pasynUserSelf);
// Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces)
char expectedResponseSubstring[MAXBUF_] = {0};
@ -457,22 +430,32 @@ asynStatus masterMacsController::parseResponse(
tcpCmd);
}
msgPrintControlKey responseMatchKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (strstr(&fullResponse[responseStart], expectedResponseSubstring) ==
NULL) {
adjustForPrint(printableCommand, fullCommand, MAXBUF_);
adjustForPrint(printableResponse, fullResponse, MAXBUF_);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) {
asynPrint(
this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line %d:\nMismatched "
"response %s to command %s\n",
"response %s to command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableResponse, printableCommand);
printableResponse, printableCommand,
msgPrintControl_.getSuffix());
}
snprintf(drvMessageText, MAXBUF_,
"Mismatched response %s to command %s. Please call the "
"support.",
printableResponse, printableCommand);
return asynError;
} else {
msgPrintControl_.resetCount(responseMatchKey, pasynUserSelf);
}
}
return asynSuccess;

View File

@ -38,7 +38,7 @@ class masterMacsController : public sinqController {
* @return masterMacsAxis* If no axis could be found, this is a
* nullptr
*/
masterMacsAxis *getAxis(asynUser *pasynUser);
masterMacsAxis *getMasterMacsAxis(asynUser *pasynUser);
/**
* @brief Get the axis object
@ -47,10 +47,7 @@ class masterMacsController : public sinqController {
* @return masterMacsAxis* If no axis could be found, this is a
* nullptr
*/
masterMacsAxis *getAxis(int axisNo);
protected:
asynUser *lowLevelPortUser_;
masterMacsAxis *getMasterMacsAxis(int axisNo);
/**
* @brief Send a command to the hardware (S mode)
@ -60,7 +57,8 @@ class masterMacsController : public sinqController {
* @param payload Value send to MasterMACS.
* @return asynStatus
*/
asynStatus write(int axisNo, int tcpCmd, const char *payload);
asynStatus write(int axisNo, int tcpCmd, const char *payload,
double comTimeout = -1.0);
/**
* @brief Send a command to the hardware and receive a response (R mode)
@ -71,7 +69,8 @@ class masterMacsController : public sinqController {
* expected to have the size MAXBUF_.
* @return asynStatus
*/
asynStatus read(int axisNo, int tcpCmd, char *response);
asynStatus read(int axisNo, int tcpCmd, char *response,
double comTimeout = -1.0);
/**
* @brief Send a command to the hardware (R or S mode) and receive a
@ -88,7 +87,7 @@ class masterMacsController : public sinqController {
* @return asynStatus
*/
asynStatus writeRead(int axisNo, int tcpCmd, const char *payload,
char *response);
char *response, double comTimeout = -1.0);
/**
* @brief Parse "fullResponse" received upon sending "fullCommand".
@ -114,35 +113,16 @@ class masterMacsController : public sinqController {
int *valueStop, int axisNo, int tcpCmd,
bool isRead);
/**
* @brief Save cast of the given asynAxis pointer to a masterMacsAxis
* pointer. If the cast fails, this function returns a nullptr.
*
* @param asynAxis
* @return masterMacsAxis*
*/
masterMacsAxis *castToAxis(asynMotorAxis *asynAxis);
private:
// Set the maximum buffer size. This is an empirical value which must be
// large enough to avoid overflows for all commands to the device /
// responses from it.
static const uint32_t MAXBUF_ = 200;
private:
/*
Stores the constructor input comTimeout
*/
double comTimeout_;
// Indices of additional PVs
#define FIRST_masterMacs_PARAM rereadEncoderPosition_
int rereadEncoderPosition_;
int readConfig_;
#define LAST_masterMacs_PARAM readConfig_
friend class masterMacsAxis;
};
#define NUM_masterMacs_DRIVER_PARAMS \
(&LAST_masterMacs_PARAM - &FIRST_masterMacs_PARAM + 1)
#endif /* masterMacsController_H */

View File

@ -2,9 +2,14 @@
Code shared by "decodeError.py" and "decodeStatus.py"
"""
import struct
def decode(value: int, interpretation):
bit_list = [int(char) for char in bin(value)[2:]]
# Pack the input as a short and unpack it as an unsigned short
value_uint16 = format(value, '016b') # Format as 16-bit unsigned integer
bit_list = [int(char) for char in value_uint16]
bit_list.reverse()
interpreted = []

View File

@ -32,109 +32,6 @@ interpretation = [
("Not specified", "Not specified"), # Bit 15
]
def interactive():
# Imported here, because curses is not available in Windows. Using the
# interactive mode therefore fails on Windows, but at least the single
# command mode can be used (which would not be possible if we would import
# curses at the top level)
import curses
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
stdscr.keypad(True)
stdscr.scrollok(True)
stdscr.addstr(">> ")
stdscr.refresh()
history = [""]
ptr = len(history) - 1
while True:
c = stdscr.getch()
if c == curses.KEY_RIGHT:
(y, x) = stdscr.getyx()
if x < len(history[ptr]) + 3:
stdscr.move(y, x+1)
stdscr.refresh()
elif c == curses.KEY_LEFT:
(y, x) = stdscr.getyx()
if x > 3:
stdscr.move(y, x-1)
stdscr.refresh()
elif c == curses.KEY_UP:
if ptr > 0:
ptr -= 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_DOWN:
if ptr < len(history) - 1:
ptr += 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_ENTER or c == ord('\n') or c == ord('\r'):
if history[ptr] == 'quit':
break
# because of arrow keys move back to the end of the line
(y, x) = stdscr.getyx()
stdscr.move(y, 3+len(history[ptr]))
if history[ptr]:
(bit_list, interpreted) = decode(history[ptr])
for (idx, (bit_value, msg)) in enumerate(zip(bit_list, interpreted)):
stdscr.addstr(f"\nBit {idx} = {bit_value}: {msg}")
stdscr.refresh()
if ptr == len(history) - 1 and history[ptr] != "":
history += [""]
else:
history[-1] = ""
ptr = len(history) - 1
stdscr.addstr("\n>> ")
stdscr.refresh()
else:
if ptr < len(history) - 1: # Modifying previous input
if len(history[-1]) == 0:
history[-1] = history[ptr]
ptr = len(history) - 1
else:
history += [history[ptr]]
ptr = len(history) - 1
if c == curses.KEY_BACKSPACE:
if len(history[ptr]) == 0:
continue
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-4] + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x-1)
stdscr.refresh()
else:
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x+1)
stdscr.refresh()
# to quit
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
curses.endwin()
if __name__ == "__main__":
from sys import argv