diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..da3868c --- /dev/null +++ b/.clang-format @@ -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 +... + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..7939390 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,49 @@ +TOP=../.. + +include $(TOP)/configure/CONFIG +#---------------------------------------- +# ADD MACRO DEFINITIONS AFTER THIS LINE +#============================= + +#============================= +# Build the IOC application + +PROD_IOC = sinqEPICS +# sinqEPICS.dbd will be created and installed +DBD += sinqEPICS.dbd + +# sinqEPICS.dbd will be made up from these files: +sinqEPICS_DBD += base.dbd + +# Include dbd files from all support applications: +sinqEPICS_DBD += sinq.dbd +#sinqEPICS_DBD += pmacAsynIPPort.dbd pmacAsynMotorPort.dbd + +# Add all the support libraries needed by this IOC +sinqEPICS_LIBS += motor asyn busy synAppsStd streamdevice pcre + +# sinqEPICS_registerRecordDeviceDriver.cpp derives from sinqEPICS.dbd +sinqEPICS_SRCS += sinqEPICS_registerRecordDeviceDriver.cpp +sinqEPICS_SRCS += EL734Driver.cpp devScalerEL737.c pmacAsynIPPort.c sinqAxis.cpp sinqController.cpp +sinqEPICS_SRCS += pmacController.cpp pmacAxis.cpp +sinqEPICS_SRCS += NanotecDriver.cpp stptok.cpp +sinqEPICS_SRCS += PhytronDriver.cpp +sinqEPICS_SRCS += EuroMoveDriver.cpp + + +# Build the main IOC entry point on workstation OSs. +sinqEPICS_SRCS_DEFAULT += sinqEPICSMain.cpp +sinqEPICS_SRCS_vxWorks += -nil- + +# Add support from base/src/vxWorks if needed +#sinqEPICS_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary + +# Finally link to the EPICS Base libraries +sinqEPICS_LIBS += $(EPICS_BASE_IOC_LIBS) + +#=========================== + +include $(TOP)/configure/RULES +#---------------------------------------- +# ADD RULES AFTER THIS LINE + diff --git a/src/sinqAxis.cpp b/src/sinqAxis.cpp new file mode 100644 index 0000000..c1794c1 --- /dev/null +++ b/src/sinqAxis.cpp @@ -0,0 +1,91 @@ +#include "sinqAxis.h" +#include "sinqController.h" +#include + +sinqAxis::sinqAxis(class sinqController *pC, int axis) + : asynMotorAxis((asynMotorController *)pC, axis), pC_(pC) { + + bool initial_poll_ = true; + int init_poll_counter_ = 0; +} + +asynStatus sinqAxis::atFirstPoll() { return asynSuccess; } + +asynStatus sinqAxis::poll(bool *moving) { + // Local variable declaration + asynStatus pl_status = asynSuccess; + asynStatus poll_status = asynSuccess; + + // ========================================================================= + + // If this poll is the initial poll, check if the parameter library has + // already been initialized. If not, force EPCIS to repeat the poll until + // the initialization is complete (or until a timeout is reached). Once the + // parameter library has been initialized, read configuration data from the + // motor controller into it. + if (initial_poll_) { + poll_status = atFirstPoll(); + if (poll_status == asynSuccess) { + initial_poll_ = false; + } else { + // Send a message to the IOC shell every 10 trials. + init_poll_counter_ += 1; + if (init_poll_counter_ % 10 == 0) { + asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, + "%s => line %d:\nRunning function 'atFirstPoll' " + "failed %d times with error %s.", + __PRETTY_FUNCTION__, __LINE__, init_poll_counter_, + pC_->stringifyAsynStatus(poll_status)); + } + + // Wait for 100 ms until trying the entire poll again + usleep(100000); + return poll_status; + } + } + + // The poll function is just a wrapper around doPoll and + // handles mainly the callParamCallbacks() function. This wrapper is used + // to make sure callParamCallbacks() is called in case of a premature + // return. + poll_status = doPoll(moving); + + // If the poll status is ok, reset the error indicators in the parameter + // library + if (poll_status == asynSuccess) { + pl_status = setIntegerParam(pC_->motorStatusProblem_, false); + if (pl_status != asynSuccess) { + pC_->paramLibAccessFailed(pl_status, "motorStatusProblem_", + __PRETTY_FUNCTION__, __LINE__); + } + pl_status = setIntegerParam(pC_->motorStatusCommsError_, false); + if (pl_status != asynSuccess) { + pC_->paramLibAccessFailed(pl_status, "motorStatusCommsError_", + __PRETTY_FUNCTION__, __LINE__); + } + + pl_status = setStringParam(pC_->motorMessageText_, ""); + if (pl_status != asynSuccess) { + return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", + __PRETTY_FUNCTION__, __LINE__); + } + } + + // According to the function documentation of asynMotorAxis::poll, this + // function should be called at the end of a poll implementation. + pl_status = callParamCallbacks(); + if (pl_status != asynSuccess) { + // 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, + "%s => line %d:\ncallParamCallbacks failed with %s for axis %d", + __PRETTY_FUNCTION__, __LINE__, + pC_->stringifyAsynStatus(poll_status), axisNo_); + poll_status = pl_status; + } + + return poll_status; +} + +asynStatus sinqAxis::doPoll(bool *moving) { return asynSuccess; } \ No newline at end of file diff --git a/src/sinqAxis.h b/src/sinqAxis.h new file mode 100644 index 0000000..7a522ca --- /dev/null +++ b/src/sinqAxis.h @@ -0,0 +1,39 @@ +/* +This class extends asynMotorAxis by some features used in SINQ. + +Stefan Mathis, November 2024 +*/ +#ifndef __SINQDRIVER +#define __SINQDRIVER +#include "asynMotorAxis.h" + +class epicsShareClass sinqAxis : public asynMotorAxis { + public: + sinqAxis(class sinqController *pC_, int axis); + + /** + This function is executed at the very first poll after the IOC startup. If + it returns anything else than 'asynSuccess', the function is evaluated again + after 100 ms until it succeeds. Every 10 trials a warning is emitted. + */ + asynStatus atFirstPoll(); + + asynStatus poll(bool *moving); + + /** + Implementation of the "proper", device-specific poll method. This method + should be implemented by a child class of sinqAxis. + */ + asynStatus doPoll(bool *moving); + + friend class sinqController; + + protected: + bool initial_poll_; + int init_poll_counter_; + + private: + sinqController *pC_; +}; + +#endif diff --git a/src/sinqController.cpp b/src/sinqController.cpp new file mode 100644 index 0000000..bf0eeb4 --- /dev/null +++ b/src/sinqController.cpp @@ -0,0 +1,124 @@ +/* + This class contains the necessary changes to have an additional text fields + for messages with each axis. + + Code lifted from Torsten Boegershausen ESS code. + + Mark Koennecke, March 2017 + + Added code to manage an interMessageSleep + + Mark Koennecke, February 2024 +*/ + +#include "sinqController.h" +#include "asynMotorController.h" +#include "epicsExport.h" +#include "iocsh.h" +#include + +sinqController::sinqController(const char *portName, const char *SINQPortName, + int numAxes, const int &extraParams) + : asynMotorController( + portName, numAxes + 1, NUM_MOTOR_DRIVER_PARAMS + extraParams, + 0, // No additional interfaces beyond those in base class + 0, // No additional callback interfaces beyond those in base class + ASYN_CANBLOCK | ASYN_MULTIDEVICE, + 1, // autoconnect + 0, 0) // Default priority and stack size +{ + createParam(motorMessageIsFromDriverString, asynParamInt32, + &motorMessageIsFromDriver_); + createParam(motorMessageTextString, asynParamOctet, &motorMessageText_); +} + +asynStatus sinqController::errMsgCouldNotParseResponse(const char *command, + const char *response, + int axisNo_, + const char *functionName, + int lineNumber) { + asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, + "%s => line %d:\n Could not interpret response %s for " + "command %s.\n", + functionName, lineNumber, response, command); + + setStringParam(motorMessageText_, + "Could not interpret MCU response. Please " + "call the software support"); + setIntegerParam(motorStatusCommsError_, 1); + return asynError; +} + +asynStatus sinqController::paramLibAccessFailed(asynStatus status, + const char *parameter, + const char *functionName, + int lineNumber) { + + if (status != asynSuccess) { + // Log the error message and try to propagate it + asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, + "%s => line %d:\n Accessing the parameter library failed for " + "parameter %s", + functionName, lineNumber, parameter); + setStringParam( + motorMessageText_, + "Accessing paramLib failed. Please call the software support."); + } + + return status; +} + +// Static pointers (valid for the entire lifetime of the IOC). The number behind +// the strings gives the integer number of each variant (see also method +// stringifyAsynStatus) +static char *asynSuccessStringified = "success"; // 0 +static char *asynTimeoutStringified = "timeout"; // 1 +static char *asynOverflowStringified = "overflow"; // 2 +static char *asynErrorStringified = "error"; // 3 +static char *asynDisconnectedStringified = "disconnected"; // 4 +static char *asynDisabledStringified = "disabled"; // 5 +static char *asynParamAlreadyExistsStringified = + "parameter already exists"; // 6 +static char *asynParamNotFoundStringified = "parameter not found"; // 7 +static char *asynParamWrongTypeStringified = "wrong type"; // 8 +static char *asynParamBadIndexStringified = "bad index"; // 9 +static char *asynParamUndefinedStringified = "parameter undefined"; // 10 +static char *asynParamInvalidListStringified = "invalid list"; // 11 + +char *sinqController::stringifyAsynStatus(asynStatus status) { + // See + // https://github.com/epics-modules/asyn/blob/master/asyn/asynDriver/asynDriver.h + // and + // https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/paramErrors.h + // for the definition of the error codes + switch (status) { + case asynSuccess: + return asynSuccessStringified; + case asynTimeout: + return asynTimeoutStringified; + case asynOverflow: + return asynOverflowStringified; + case asynError: + return asynErrorStringified; + case asynDisconnected: + return asynDisconnectedStringified; + case asynDisabled: + return asynDisabledStringified; + case asynParamAlreadyExists: + return asynParamAlreadyExistsStringified; + case asynParamNotFound: + return asynParamNotFoundStringified; + case asynParamWrongType: + return asynParamWrongTypeStringified; + case asynParamBadIndex: + return asynParamBadIndexStringified; + case asynParamUndefined: + return asynParamUndefinedStringified; + case asynParamInvalidList: + return asynParamInvalidListStringified; + } + + errlogPrintf("%s => line %d:\nReached unreachable code.", + __PRETTY_FUNCTION__, __LINE__); + return "unreachable code reached"; +} \ No newline at end of file diff --git a/src/sinqController.h b/src/sinqController.h new file mode 100644 index 0000000..07412f4 --- /dev/null +++ b/src/sinqController.h @@ -0,0 +1,54 @@ +/* + This class contains the necessary changes to have an additional text fields + for messages with each axis. + + Code lifted from Torsten Boegershausens ESS code. + + Mark Koennecke, March 2017 +*/ + +#ifndef __sinqController +#define __sinqController +#include "asynMotorController.h" + +#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER" +#define motorMessageTextString "MOTOR_MESSAGE_TEXT" + +class epicsShareClass sinqController : public asynMotorController { + public: + sinqController(const char *portName, const char *SINQPortName, int numAxes, + const int &extraParams = 2); + + friend class sinqAxis; + + /** + If accessing the parameter library failed (return status != asynSuccess), + calling this function writes a standardized message to both the IOC shell + and the motor message text PV. It then returns the input status. + */ + asynStatus paramLibAccessFailed(asynStatus status, const char *parameter, + const char *functionName, int lineNumber); + + /** + This function writes a standardized message to both the IOC shell and + the motor message text PV in case parsing a response (e.g. via sscanf) + failed. It always returns asynError. + */ + asynStatus errMsgCouldNotParseResponse(const char *command, + const char *response, int axisNo_, + const char *functionName, + int lineNumber); + + /** + Convert an asynStatus into a descriptive string. This string can then e.g. + be used to create debugging messages. + */ + char *stringifyAsynStatus(asynStatus status); + + protected: + asynUser *lowLevelPortUser_; + int motorMessageIsFromDriver_; + int motorMessageText_; +}; + +#endif