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:
image: docker.psi.ch:5000/sinqdev/sinqepics:latest
stages:
- lint
- build
@ -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.8.0
# These headers allow to depend on this library for derived drivers.
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):
- `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) {
@ -340,8 +368,9 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
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;
@ -362,70 +391,26 @@ 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,
"Controller \"%s\", axis %d => %s, line "
"%d\nCommunication error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nCommunication error.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
pl_status =
setStringParam(pC_->motorMessageText_,
@ -438,172 +423,164 @@ asynStatus masterMacsAxis::doPoll(bool *moving) {
}
poll_status = asynError;
}
} else {
if (feedbackError()) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line "
"%d\nFeedback error.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
// 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};
pl_status =
setStringParam(pC_->motorMessageText_,
"Feedback error. Please call the support.");
// 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()) {
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) {
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.
*/
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

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;

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
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);
}
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;
return dynamic_cast<masterMacsAxis *>(asynAxis);
}
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) {
// We already did the error logging directly in getAxis
return asynError;
@ -248,17 +227,17 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
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);
msgPrintControlKey writeKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynSuccess) {
msgPrintControl_.resetCount(writeKey);
// Try to read the answer repeatedly
int maxTrials = 2;
@ -294,15 +273,18 @@ asynStatus masterMacsController::writeRead(int axisNo, int tcpCmd,
}
} else {
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));
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.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
}
}
// MasterMACS needs a bit of time between messages, therefore thr program
// execution is paused after the communication happened.
// 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
@ -345,10 +327,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 +386,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 +403,39 @@ asynStatus masterMacsController::parseResponse(
} else if (fullResponse[i] == '\x15') {
// NAK
snprintf(drvMessageText, MAXBUF_, "Communication failed.");
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line "
"%d:\nCommunication failed\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (msgPrintControl_.shouldBePrinted(parseKey, true,
pasynUserSelf)) {
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;
} 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,
"Controller \"%s\", axis %d => %s, line %d:\nTried to "
"write with the read-only command %s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableCommand);
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.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableCommand, msgPrintControl_.getSuffix());
}
responseValid = false;
break;
}
}
if (responseValid) {
msgPrintControl_.resetCount(parseKey);
// Check if the response matches the expectations. Each response
// contains the string "axisNo R tcpCmd" (including the spaces)
char expectedResponseSubstring[MAXBUF_] = {0};
@ -457,22 +450,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,
"Controller \"%s\", axis %d => %s, line %d:\nMismatched "
"response %s to command %s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
printableResponse, printableCommand);
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.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
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);
}
}
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,7 +47,7 @@ class masterMacsController : public sinqController {
* @return masterMacsAxis* If no axis could be found, this is a
* nullptr
*/
masterMacsAxis *getAxis(int axisNo);
masterMacsAxis *getMasterMacsAxis(int axisNo);
protected:
asynUser *lowLevelPortUser_;
@ -114,15 +114,6 @@ 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 /

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