commit 4d809d11e064982be9ef50a391d356a5956e8ad4 Author: Hugo Jean Ponsin Date: Tue Apr 28 08:20:38 2026 +0200 first ePowerSwitch verion diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..577a65d --- /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/.gitignore b/.gitignore new file mode 100644 index 0000000..f30446f --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.vscode/ +CMakeCache.txt +CMakeFiles +Makefile +cmake_install.cmake +midas.log diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a77c09a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "submodule/mepicsca"] + path = submodule/mepicsca + url = git@gitea.psi.ch:lin-midas-drivers/mepicsca.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..09132ff --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.03) + +project(ePowerSwitchFront VERSION 1.0) + +add_subdirectory(submodule/mepicsca) + +add_compile_options( + -Wall + -Wformat=2 + -g + -Wno-format-nonliteral + -Wno-strict-aliasing + -Wuninitialized + -Wno-unused-function +) + +# Check if the required environment variables MIDASSYS and EPICSSYS are available +if (NOT DEFINED ENV{MIDASSYS}) + message(SEND_ERROR "MIDASSYS environment variable not defined.") +endif() +if (NOT DEFINED ENV{EPICSSYS}) + message(SEND_ERROR "EPICSSYS environment variable not defined.") +endif() + +set(CMAKE_CXX_STANDARD 17) +set(MIDASSYS $ENV{MIDASSYS}) +set(EPICSSYS $ENV{EPICSSYS}) + +# Select the correct EPICS library depending on the OS (currently Linus or Darwin are available) +if (${CMAKE_SYSTEM_NAME} MATCHES Linux) + link_directories(${EPICSSYS}/lib/linux-x86_64) + set(LIBS ${LIBS} -lpthread -lutil -lrt -lbsd -ldl) +endif() +if (${CMAKE_SYSTEM_NAME} MATCHES Darwin) + link_directories(${EPICSSYS}/lib/darwin-aarch64) + set(LIBS ${LIBS} -lutil -lca) +endif() + +set(LIBS + ${LIBS} + ${EPICSSYS}/lib/$ENV{EPICS_HOST_ARCH}/libca.so + ${EPICSSYS}/lib/$ENV{EPICS_HOST_ARCH}/libCom.so # For alarm.h + $ENV{MIDASSYS}/lib/libmfe.a + $ENV{MIDASSYS}/lib/libmidas.a + $ENV{MIDASSYS}/lib/libmscb.a +) + +find_package(Midas REQUIRED) + +add_executable(ePowerSwitchFront + src/ePowerSwitchFront.cpp +) + +set_property( + TARGET + ePowerSwitchFront + PROPERTY + CXX_STANDARD 17 +) + + +target_include_directories( + ePowerSwitchFront + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE + $ENV{MIDASSYS}/drivers + $ENV{MIDASSYS}/include + + #$ENV{MIDASSYS}/drivers + #$ENV{MIDASSYS}/include + # This is redundant if MIDASSYS =/ Midas repo, but needed if MIDAS has been + # installed in the repo directory, since the CMake file of MIDAS doesn't + # copy the headers into $ENV{MIDASSYS}/include in the latter case. + $ENV{MIDASSYS}/include/mscb + ${EPICSSYS}/include + ${EPICSSYS}/include/os/Linux + ${EPICSSYS}/include/os/Darwin + ${EPICSSYS}/include/compiler/gcc + ${EPICSSYS}/include/compiler/clang +) + +target_link_libraries(ePowerSwitchFront + PRIVATE + midas::mfe + m_epics_ca + ${LIBS} +) + +install( + TARGETS + ePowerSwitchFront + RUNTIME DESTINATION bin +) + +# Run the executable during the install process to register it in MIDAS +# include(register_exe) +# register_executable_once("${CMAKE_INSTALL_PREFIX}/bin/ePowerSwitchFront") diff --git a/include/ePowerSwitchConfig.h b/include/ePowerSwitchConfig.h new file mode 100644 index 0000000..509f590 --- /dev/null +++ b/include/ePowerSwitchConfig.h @@ -0,0 +1,7 @@ +#ifndef EPOWERSWITCH_CONFIG_H +#define EPOWERSWITCH_CONFIG_H + +#define EPOWERSWITCH_SOCKET_COMMAND_PREFIX "ePowerSwitch_command_outlet_" +#define EPOWERSWITCH_SOCKETNUMBER_INFO_PREFIX "ePowerSwitch_config_maxOutlet" + +#endif diff --git a/include/ePowerSwitchFront.h b/include/ePowerSwitchFront.h new file mode 100644 index 0000000..3160d3a --- /dev/null +++ b/include/ePowerSwitchFront.h @@ -0,0 +1,60 @@ +#ifndef EPOWERSWITCH_FRONTEND_H +#define EPOWERSWITCH_FRONTEND_H + +#include "m_epics_ca.h" +#include "tmfe.h" +#include + +class ePowerSwitchEquipment : public TMFeEquipment { + + public: + /** + * @brief contructor + * + * @param equipmentName name show on midas for the equipment + * @param equipmentFileName file name for automatic tagged message + */ + ePowerSwitchEquipment(std::string equipmentName, + const char *equipmentFileName); + + /** + * @brief call periodicly by midas + */ + void HandlePeriodic(); + + private: + std::vector *> outletCommandRecords; + std::vector outletLastState; + mEpicsCa outletNumberRecord; + int numberOfOutlet; + + /** + * @brief refresh information about the given socket id + * + * @param socketId which socket to refresh + */ + void refreshSocket(int socketId); + + /** + * @brief refresh information about all the available socket + */ + void refreshAllSockets(); + + /** + * @brief detect and modify the current socket pool in case of change + */ + void updateSocketNumber(); +}; + +class ePowerSwitchFrontend : public TMFrontend { + public: + /** + * @brief contructor + * + * @param frontendName name show on midas for the frontend + * @param equipmentName name show on midas for the equipment + */ + ePowerSwitchFrontend(std::string frontendName, std::string equipmentName); +}; + +#endif \ No newline at end of file diff --git a/src/ePowerSwitchFront.cpp b/src/ePowerSwitchFront.cpp new file mode 100644 index 0000000..ed5e53d --- /dev/null +++ b/src/ePowerSwitchFront.cpp @@ -0,0 +1,118 @@ +#include "ePowerSwitchFront.h" +#include "ePowerSwitchConfig.h" +#include "m_epics_ca.h" +#include "tmfe.h" +#include +#include + +/** + * @brief local function, append an integer at the end of a string + * @param str string prfix + * @param value integer to append + * @return "str" + integer + */ +std::string format(std::string str, int value) { + std::stringstream ss; + ss << str; + ss << value; + return ss.str(); +} + +int main(int argc, char *argv[]) { + std::string frontendName = "ePowerSwitch"; + std::string equipmentName = "powerswitch"; + + if (argc == 1) { + frontendName = std::string(argv[1]); + argv++; + argc--; + } else if (argc == 2) { + frontendName = std::string(argv[1]); + equipmentName = std::string(argv[2]); + argv += 2; + argc -= 2; + } + + auto front = new ePowerSwitchFrontend(frontendName.c_str()); + front->FeMain(argc, argv); +} + +ePowerSwitchEquipment::ePowerSwitchEquipment(std::string equipmentName, + const char *equipmentFileName) + : TMFeEquipment(equipmentName, equipmentFileName), + outletNumberRecord( + mEpicsCa(EPOWERSWITCH_SOCKETNUMBER_INFO_PREFIX, true)) { + fEqConfReadConfigFromOdb = false; + fEqConfPeriodMilliSec = 1000; + fEqConfLogHistory = 1; + fEqConfReadOnlyWhenRunning = false; + fEqConfWriteEventsToOdb = true; + + this->numberOfOutlet = 0; +} + +void ePowerSwitchEquipment::HandlePeriodic() { + updateSocketNumber(); + refreshAllSockets(); +} + +void ePowerSwitchEquipment::refreshSocket(int socketId) { + std::string varname = format("Socket ", socketId); + + int midasSocketState = 0; // default => turn off + fOdbEqVariables->RI(varname.c_str(), &midasSocketState, true); + + if (midasSocketState != this->outletLastState.at(socketId)) { + this->outletCommandRecords.at(socketId)->put(&midasSocketState); + printf("send command : %d\n", midasSocketState); + } + + this->outletLastState.at(socketId) = midasSocketState; +} + +void ePowerSwitchEquipment::refreshAllSockets() { + for (int i = 0; i < this->numberOfOutlet; i++) { + refreshSocket(i); + } +} + +void ePowerSwitchEquipment::updateSocketNumber() { + int epicsOutletNumber = 0; + + if (!outletNumberRecord.connected()) + return; + int returnCode = outletNumberRecord.get(&epicsOutletNumber); + + if (returnCode < 0) { + printf("error\n"); + return; + } + + for (int i = this->numberOfOutlet; i < epicsOutletNumber; i++) { + std::string epicsRecordName = + format(EPOWERSWITCH_SOCKET_COMMAND_PREFIX, i); + + this->outletCommandRecords.push_back( + new mEpicsCa(epicsRecordName.c_str(), true)); + this->outletLastState.push_back(0); + + printf("new socket created\n"); + } + + for (int i = epicsOutletNumber; i < this->numberOfOutlet; i++) { + mEpicsCa *ptr = this->outletCommandRecords.back(); + delete ptr; + + this->outletCommandRecords.pop_back(); + this->outletLastState.pop_back(); + printf("old socket destroyed\n"); + } + + this->numberOfOutlet = epicsOutletNumber; +} + +ePowerSwitchFrontend::ePowerSwitchFrontend(std::string frontendName, + std::string equipmentName) { + FeSetName(frontname.c_str()); + FeAddEquipment(new ePowerSwitchEquipment(equipmentName.c_str(), __FILE__)); +} \ No newline at end of file diff --git a/submodule/mepicsca b/submodule/mepicsca new file mode 160000 index 0000000..d9e1789 --- /dev/null +++ b/submodule/mepicsca @@ -0,0 +1 @@ +Subproject commit d9e17892ba2e85beff98690d4c4b10d7b93821f3