5 Commits
0.1.0 ... 0.2.0

Author SHA1 Message Date
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 549 additions and 420 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

@ -1,6 +1,6 @@
default: default:
image: docker.psi.ch:5000/sinqdev/sinqepics:latest image: docker.psi.ch:5000/sinqdev/sinqepics:latest
stages: stages:
- lint - lint
- build - build
@ -9,7 +9,7 @@ stages:
cppcheck: cppcheck:
stage: lint stage: lint
script: 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: artifacts:
expire_in: 1 week expire_in: 1 week
tags: tags:
@ -24,16 +24,6 @@ formatting:
tags: tags:
- sinq - 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: build_module:
stage: build stage: build
script: script:

View File

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

View File

@ -18,22 +18,42 @@ The folder "utils" contains utility scripts for working with masterMacs motor co
masterMacs exposes the following IOC shell functions (all in masterMacsController.cpp): masterMacs exposes the following IOC shell functions (all in masterMacsController.cpp):
- `masterMacsController`: Create a new controller object. - `masterMacsController`: Create a new controller object.
- `masterMacsAxis`: Create a new axis object. - `masterMacsAxis`: Create a new axis object.
These functions are parametrized as follows:
The full mcu.cmd file looks like this:
``` ```
masterMacsController( # Define the name of the controller and the corresponding port
"$(NAME)", # Name of the MCU, e.g. mcu1. This parameter should be provided by an environment variable. epicsEnvSet("NAME","mcu")
"$(ASYN_PORT)", # IP-Port of the MCU. This parameter should be provided by an environment variable. epicsEnvSet("ASYN_PORT","p$(NAME)")
8, # Maximum number of axes
0.05, # Busy poll period in seconds # 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
1, # Idle poll period in seconds drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
0.05 # Communication timeout in seconds
); # 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
masterMacsAxis( # 0.05: Busy poll period in seconds
"$(NAME)", # Name of the associated MCU, e.g. mcu1. This parameter should be provided by an environment variable. # 1: Idle poll period in seconds
1, # Index of the axis. # 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 ### 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) masterMacsAxis::masterMacsAxis(masterMacsController *pC, int axisNo)
: sinqAxis(pC, axisNo), pC_(pC) { : sinqAxis(pC, axisNo), pC_(pC) {
@ -340,8 +368,9 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
return rw_status; return rw_status;
} }
// Check if we reached the target // If the motor is switched off or if it reached its target, it is not
if (targetReached()) { // moving.
if (targetReached() || !switchedOn()) {
*moving = false; *moving = false;
} else { } else {
*moving = true; *moving = true;
@ -362,70 +391,26 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
Read out the error if either a fault condition status flag has been set or Read out the error if either a fault condition status flag has been set or
if a movement has just ended. if a movement has just ended.
*/ */
if (faultConditionSet()) { msgPrintControlKey keyError = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (faultConditionSet() || !(*moving)) {
rw_status = readAxisError(); rw_status = readAxisError();
if (shortCircuit()) { /*
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, A communication error is a special case. If a communication between
"Controller \"%s\", axis %d => %s, line %d\nShort " controller and axis error occurred, all subsequent errors are ignored,
"circuit fault.\n", since this information is not reliable.
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;
}
if (communicationError()) { if (communicationError()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line " pC_->pasynUserSelf)) {
"%d\nCommunication error.\n", asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "Controller \"%s\", axis %d => %s, line "
"%d\nCommunication error.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
pl_status = pl_status =
setStringParam(pC_->motorMessageText_, setStringParam(pC_->motorMessageText_,
@ -438,172 +423,164 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
} }
poll_status = asynError; poll_status = asynError;
} } else {
if (feedbackError()) { // This buffer must be initialized to zero because we build the
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, // error message by appending strings.
"Controller \"%s\", axis %d => %s, line " char userMessage[pC_->MAXBUF_] = {0};
"%d\nFeedback error.\n", char shellMessage[pC_->MAXBUF_] = {0};
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status = // Concatenate all other errors
setStringParam(pC_->motorMessageText_, if (shortCircuit()) {
"Feedback error. Please call the support."); 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()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Feedback error.");
appendErrorMessage(userMessage, sizeof(userMessage),
"Feedback error. Please call the support.");
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.
*/
if (positiveLimitSwitch() || negativeLimitSwitch() ||
positiveSoftwareLimit() || negativeSoftwareLimit()) {
// Distinction for developers
if (positiveLimitSwitch()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Positive limit switch.");
}
if (negativeLimitSwitch()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Negative limit switch.");
}
if (positiveSoftwareLimit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Positive software limit.");
}
if (negativeSoftwareLimit()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Negative software limit.");
}
// Generic error message for user
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.");
poll_status = asynError;
}
if (overCurrent()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overcurrent error.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Overcurrent error. Please call the support.");
poll_status = asynError;
}
if (overTemperature()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overtemperature error.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Overtemperature error. Please call the support.");
poll_status = asynError;
}
if (overVoltage()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Overvoltage error.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Overvoltage error. Please call the support.");
poll_status = asynError;
}
if (underVoltage()) {
appendErrorMessage(shellMessage, sizeof(shellMessage),
"Undervoltage error.");
appendErrorMessage(
userMessage, sizeof(userMessage),
"Undervoltage error. Please call the support.");
poll_status = asynError;
}
if (stoFault()) {
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_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\n%s.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__,
__LINE__, shellMessage,
pC_->msgPrintControl_.getSuffix());
}
}
pl_status = setStringParam(pC_->motorMessageText_, userMessage);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
__LINE__); __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.
*/
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__);
}
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__);
}
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__);
}
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__);
}
// Generic error message for user
pl_status = setStringParam(
pC_->motorMessageText_,
"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_,
"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_,
"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_,
"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_,
"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_,
"STO fault. Please call the support.");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
poll_status = asynError;
} }
} else {
pC_->msgPrintControl_.resetCount(keyError);
} }
// Read out the limits, if the motor is not moving // Read out the limits, if the motor is not moving

View File

@ -4,7 +4,7 @@
#include <bitset> #include <bitset>
// Forward declaration of the controller class to resolve the cyclic dependency // 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. // https://en.cppreference.com/w/cpp/language/class.
class masterMacsController; class masterMacsController;

View File

@ -117,39 +117,18 @@ 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 If the axis does not exist or is not a Axis, a nullptr is returned and an
error is emitted. error is emitted.
*/ */
masterMacsAxis *masterMacsController::getAxis(asynUser *pasynUser) { masterMacsAxis *masterMacsController::getMasterMacsAxis(asynUser *pasynUser) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(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. 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 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); 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) {
@ -195,7 +174,7 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// ========================================================================= // =========================================================================
masterMacsAxis *axis = getAxis(axisNo); masterMacsAxis *axis = getMasterMacsAxis(axisNo);
if (axis == nullptr) { if (axis == nullptr) {
// We already did the error logging directly in getAxis // We already did the error logging directly in getAxis
return asynError; return asynError;
@ -248,17 +227,17 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
fullCommand[2] = lenWithMetadataSep.quot; // MSB fullCommand[2] = lenWithMetadataSep.quot; // MSB
adjustForPrint(printableCommand, fullCommand, MAXBUF_); 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 // Send out the command
status = status =
pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand, pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
fullCommandLength, comTimeout_, &nbytesOut); fullCommandLength, comTimeout_, &nbytesOut);
msgPrintControlKey writeKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynSuccess) { if (status == asynSuccess) {
msgPrintControl_.resetCount(writeKey);
// Try to read the answer repeatedly // Try to read the answer repeatedly
int maxTrials = 2; int maxTrials = 2;
@ -294,15 +273,18 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
} }
} else { } else {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d:\nError %s while " asynPrint(
"writing to the controller\n", this->pasynUserSelf, ASYN_TRACE_ERROR,
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, "Controller \"%s\", axis %d => %s, line %d:\nError %s while "
stringifyAsynStatus(status)); "writing to the controller.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
}
} }
// MasterMACS needs a bit of time between messages, therefore thr program // MasterMACS needs a bit of time between messages, therefore thr
// execution is paused after the communication happened. // program execution is paused after the communication happened.
// usleep(1500); // usleep(1500);
// Create custom error messages for different failure modes // Create custom error messages for different failure modes
@ -345,10 +327,6 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
// Log the overall status (communication successfull or not) // Log the overall status (communication successfull or not)
if (status == asynSuccess) { if (status == asynSuccess) {
adjustForPrint(printableResponse, fullResponse, MAXBUF_); 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); pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
} else { } else {
@ -408,6 +386,9 @@ asynStatus masterMacsController::parseResponse(
char printableCommand[MAXBUF_] = {0}; char printableCommand[MAXBUF_] = {0};
char printableResponse[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 // We don't use strlen here since the C string terminator 0x00
// occurs in the middle of the char array. // occurs in the middle of the char array.
for (uint32_t i = 0; i < MAXBUF_; i++) { for (uint32_t i = 0; i < MAXBUF_; i++) {
@ -422,27 +403,39 @@ asynStatus masterMacsController::parseResponse(
} else if (fullResponse[i] == '\x15') { } else if (fullResponse[i] == '\x15') {
// NAK // NAK
snprintf(drvMessageText, MAXBUF_, "Communication failed."); snprintf(drvMessageText, MAXBUF_, "Communication failed.");
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line " if (msgPrintControl_.shouldBePrinted(parseKey, true,
"%d:\nCommunication failed\n", pasynUserSelf)) {
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d:\nCommunication failed.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
msgPrintControl_.getSuffix());
}
break; break;
} else if (fullResponse[i] == '\x18') { } else if (fullResponse[i] == '\x18') {
// CAN // CAN
snprintf(drvMessageText, MAXBUF_, snprintf(drvMessageText, MAXBUF_,
"Tried to write with a read-only command. This is a " "Tried to write with a read-only command. This is a "
"bug, please call the support."); "bug, please call the support.");
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nTried to " if (msgPrintControl_.shouldBePrinted(parseKey, true,
"write with the read-only command %s\n", pasynUserSelf)) {
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, asynPrint(
printableCommand); this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nTried to "
"write with the read-only command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableCommand, msgPrintControl_.getSuffix());
}
responseValid = false; responseValid = false;
break; break;
} }
} }
if (responseValid) { if (responseValid) {
msgPrintControl_.resetCount(parseKey);
// Check if the response matches the expectations. Each response // Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces) // contains the string "axisNo R tcpCmd" (including the spaces)
char expectedResponseSubstring[MAXBUF_] = {0}; char expectedResponseSubstring[MAXBUF_] = {0};
@ -457,22 +450,32 @@ asynStatus masterMacsController::parseResponse(
tcpCmd); tcpCmd);
} }
msgPrintControlKey responseMatchKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (strstr(&fullResponse[responseStart], expectedResponseSubstring) == if (strstr(&fullResponse[responseStart], expectedResponseSubstring) ==
NULL) { NULL) {
adjustForPrint(printableCommand, fullCommand, MAXBUF_); adjustForPrint(printableCommand, fullCommand, MAXBUF_);
adjustForPrint(printableResponse, fullResponse, MAXBUF_); adjustForPrint(printableResponse, fullResponse, MAXBUF_);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, if (msgPrintControl_.shouldBePrinted(parseKey, true,
"Controller \"%s\", axis %d => %s, line %d:\nMismatched " pasynUserSelf)) {
"response %s to command %s\n", asynPrint(
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
printableResponse, printableCommand); "Controller \"%s\", axis %d => %s, line %d:\nMismatched "
"response %s to command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableResponse, printableCommand,
msgPrintControl_.getSuffix());
}
snprintf(drvMessageText, MAXBUF_, snprintf(drvMessageText, MAXBUF_,
"Mismatched response %s to command %s. Please call the " "Mismatched response %s to command %s. Please call the "
"support.", "support.",
printableResponse, printableCommand); printableResponse, printableCommand);
return asynError; return asynError;
} else {
msgPrintControl_.resetCount(responseMatchKey);
} }
} }
return asynSuccess; return asynSuccess;

View File

@ -38,7 +38,7 @@ class masterMacsController : public sinqController {
* @return masterMacsAxis* If no axis could be found, this is a * @return masterMacsAxis* If no axis could be found, this is a
* nullptr * nullptr
*/ */
masterMacsAxis *getAxis(asynUser *pasynUser); masterMacsAxis *getMasterMacsAxis(asynUser *pasynUser);
/** /**
* @brief Get the axis object * @brief Get the axis object
@ -47,7 +47,7 @@ class masterMacsController : public sinqController {
* @return masterMacsAxis* If no axis could be found, this is a * @return masterMacsAxis* If no axis could be found, this is a
* nullptr * nullptr
*/ */
masterMacsAxis *getAxis(int axisNo); masterMacsAxis *getMasterMacsAxis(int axisNo);
protected: protected:
asynUser *lowLevelPortUser_; asynUser *lowLevelPortUser_;
@ -114,15 +114,6 @@ class masterMacsController : public sinqController {
int *valueStop, int axisNo, int tcpCmd, int *valueStop, int axisNo, int tcpCmd,
bool isRead); 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: private:
// Set the maximum buffer size. This is an empirical value which must be // Set the maximum buffer size. This is an empirical value which must be
// large enough to avoid overflows for all commands to the device / // large enough to avoid overflows for all commands to the device /

View File

@ -2,9 +2,14 @@
Code shared by "decodeError.py" and "decodeStatus.py" Code shared by "decodeError.py" and "decodeStatus.py"
""" """
import struct
def decode(value: int, interpretation): 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() bit_list.reverse()
interpreted = [] interpreted = []

View File

@ -32,109 +32,6 @@ interpretation = [
("Not specified", "Not specified"), # Bit 15 ("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__": if __name__ == "__main__":
from sys import argv from sys import argv