commit 4e2d16afb12e85e48c49d76a08ccd42db47fcc06 Author: smathis Date: Tue Nov 4 16:17:39 2025 +0100 Initial working commit 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..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6b59032 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,111 @@ +cmake_minimum_required(VERSION 3.03) + +project(hvr800_scfe VERSION 0.1) + +add_compile_options( + -Wall + -Wformat=2 + -Wno-format-nonliteral + -Wno-strict-aliasing + -Wuninitialized + -Wno-unused-function +) + +option( + BUILD_FRONTEND + "If ON, build the default frontend executable" + OFF +) + + +set( + DRIVERS + $ENV{MIDASSYS}/drivers/bus/null.cxx + $ENV{MIDASSYS}/drivers/class/multi.cxx + $ENV{MIDASSYS}/drivers/class/hv.cxx + $ENV{MIDASSYS}/mscb/src/mscb.cxx +) + +set( + LIBS + # -lz # compression library + -lpthread + -lutil + -lrt # librt for real time guarantees (really just backwards compat) + -ldl # libdl for dynamic loading + $ENV{MIDASSYS}/lib/libmfe.a + $ENV{MIDASSYS}/lib/libmidas.a +) + +################################################################################ +## Device Library +################################################################################ + +add_library( + hvr800 + device/hvr800.cxx + ${DRIVERS} +) + +set_property( + TARGET + hvr800 + PROPERTY + CXX_STANDARD 11 +) + +target_include_directories( + hvr800 + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE + $ENV{MIDASSYS}/drivers + $ENV{MIDASSYS}/include + $ENV{MIDASSYS}/mscb/include +) + +target_link_libraries( + hvr800 + ${LIBS} +) + +################################################################################ +## Test Frontend +################################################################################ + +if(${BUILD_FRONTEND}) + + add_executable( + hvr800_fe + frontend/hvr800_scfe.cxx + ) + + set_property( + TARGET + hvr800_fe + PROPERTY + CXX_STANDARD 11 + ) + + target_include_directories( + hvr800_fe + PRIVATE + $ENV{MIDASSYS}/drivers + $ENV{MIDASSYS}/include + ) + + target_link_libraries( + hvr800_fe + hvr800 + ) + +endif() + +# Running this executable for the first time "registers" it in the ODB +add_custom_target(init_hvr800_scfe + # The -e flag specifies the experiment name + # is a generator expression which provided the full path to the frontend executable + # Any errors returned by the frontend executable should be ignored + COMMAND timeout --preserve-status 2s $ -e ${MIDAS_EXPTAB_NAME} || true + COMMENT "Initial run of frontend to register it in the ODB" +) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0cf9183 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# MSCB (Midas slow control bus) high voltage divider HVR 800 voltage + +This repository contains the driver code and "default" Midas frontend for the +Midas slow control bus (MSCB) high voltage divider. + +## Requirements + +Requires a Midas installation with the environment variable `MIDASSYS` pointing +to the built Midas artifacts and headers. + +## Build + +Clone this repository, enter the cloned directory, and then build via CMake. + +```bash +cmake -S "$(pwd)" -B -DBUILD_FRONTEND=ON +cmake --build --clean-first -- -j8 +``` + +## Run + +```bash +/build/driver/hvr800_fe -e flame +``` + +## Including in Custom Frontend + +To include this driver in a custom frontend, you should ensure the environment +variable `MIDASSYS` points to your system install and include a snippet in your +`CMakeLists.txt` similar to the following: + +```CMake +# If the path to the hvr800 repository is outside of the frontend +# repository, you also need to provide a location for the hvr800 +# artifacts as a second argument to `add_subdirectory` +# e.g. `./device/hvr800` +add_subdirectory( + +) + +add_executable( + + + + : +) + +set_property( + TARGET + + PROPERTY + CXX_STANDARD 11 +) + +target_include_directories( + + PRIVATE + $ENV{MIDASSYS}/drivers + $ENV{MIDASSYS}/include +) + +target_link_libraries( + + hvr800 + + + : +) +``` diff --git a/device/hvr800.cxx b/device/hvr800.cxx new file mode 100644 index 0000000..4796364 --- /dev/null +++ b/device/hvr800.cxx @@ -0,0 +1,804 @@ +/*---------------------------------------------------------------------------- + + Name: hvr800.cxx + Created by: Andreas Suter 2009/09/25 + Modified by: Jonas A. Krieger 2024/02/12 (ported to c++) + + Contents: MIDAS device driver declaration for the MSCB high voltage + dividers hvr_800. + +----------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include "midas.h" +#include "mfe.h" +#include "msystem.h" +#include "mscb.h" + +#include "hvr800.h" + +// --------- to handle error messages ------------------------------ + +#define HVR800_INIT_ERROR -2 //!< tag: initializing error + +#define HVR800_MAX_ERROR 5 //!< maximum number of error messages +#define HVR800_DELTA_TIME_ERROR 600 //!< reset error counter after DELTA_TIME_ERROR seconds + +// ----------- HVR800 related infos --------------------------------- +//! MSCB debug flag +#define MSCB_DEBUG FALSE + +// HVR800 offsets --------------------------------------------------- +#define HVR800_CONTROL 0 +#define HVR800_VDEMAND 1 +#define HVR800_VMEAS 2 +#define HVR800_IMEAS 3 +#define HVR800_STATUS 4 +#define HVR800_TRIPCNT 5 +#define HVR800_RAMPUP 6 +#define HVR800_RAMPDOWN 7 +#define HVR800_VLIMIT 8 +#define HVR800_ILIMIT 9 +#define HVR800_RILIMIT 10 +#define HVR800_TRIPMAX 11 +#define HVR800_TRIPTIME 12 + +// HVR800 Control Bits ---------------------------------------------- +#define HVR800_CTRL_HV_ON 0x01 +#define HVR800_CTRL_REGULATION 0x02 +#define HVR800_CTRL_IDLE 0x04 + +/*! + *

Stores all the parameters the device driver needs. + */ +typedef struct { + BOOL enabled; //!< flag indicating if the device is enabled or disabled + INT detailed_msg; //!< flag indicating if detailed status/error messages are wanted + char port[NAME_LENGTH]; //!< MSCB port, e.g. /dev/parport0 or usb1 + char pwd[NAME_LENGTH]; //!< MSCB password for MSCB ethernet modules + INT group_addr; //!< group address within the MSCB + INT node_addr; //!< node address of the first HV channel within the MSCB + char name[NAME_LENGTH]; //!< name of the hvr_800 card + INT odb_offset; //!< HV channel offset within ODB +} HVR800_SETTINGS; + +//! Initializing string for the struct HVR800_SETTINGS +#define HVR800_SETTINGS_STR "\ +Enabled = BOOL : 0\n\ +Detailed Messages = INT : 0\n\ +MSCB Port = STRING : [32] usb1\n\ +MSCB Pwd = STRING : [32]\n\ +Group Addr = INT : 800\n\ +Node Addr = INT : 1\n\ +HVR800 Name = STRING : [32] HVR800_1\n\ +HV ODB Offset = INT : 0\n\ +" + +//------------------------------------------------------------------ +/*! + *

Variable set of a hvr800 channel. + * + *

The control byte can be or'ed with the following values: + * - 0x0 HV off + * - 0x1 HV on + * - 0x2 HV regulated + * - 0x4 HV idle + * + *

The status byte can be or'ed with the following states: + * - 0x00 happy (??) + * - 0x01 negative (??) + * - 0x02 low current + * - 0x04 ramping up + * - 0x08 ramping down + * - 0x10 voltage limit reached + * - 0x20 current limit reached + */ +typedef struct { + unsigned char control; //!< control tag + float u_demand; //!< demand HV in (V) + float u_meas; //!< measured HV in (V) + float i_meas; //!< measured current in (uA) + unsigned char status; //!< status tag + unsigned char trip_cnt; //!< trip counter + + float ramp_up; //!< ramp up speed in (V/s) + float ramp_down; //!< ramp down speed in (V/s) + float u_limit; //!< voltage limit in (V) + float i_limit; //!< current limit in (uA) + float ri_limit; //!< ?? + unsigned char trip_max; //!< maximal allowed number of HV trips + unsigned char trip_time; //!< ?? + + unsigned char cached; //!< cache flag +} HVR800_NODE_VARS; + + +//! This structure contains private variables for the device driver. +typedef struct { + HVR800_SETTINGS settings; //!< stores the internal DD settings + HVR800_NODE_VARS *node_vars; //!< stores the variables of all HVR800 node + INT fd; //!< MSCB file desciptor + INT errcount; //!< error counter in order not to flood the message queue + INT startup_error; //!< initializer error tag, if set, hvr800_get and hvr800_set won't do anything + DWORD lasterrtime; //!< last error time stamp +} HVR800_INFO; + + +// device driver support routines ------------------------------------------------- +/*! + *

+ * + * \param info + * \param channel + */ +INT hvr800_read_all(HVR800_INFO* info, int channel) +{ + int size, status; + unsigned char buffer[256], *pbuf; + + size = sizeof(buffer); + status = mscb_read_range(info->fd, info->settings.node_addr+channel, 0, 12, buffer, &size); + if (status != MSCB_SUCCESS) { + cm_msg(MERROR, "hvr800_read_all", + "Cannot access MSCB HVR800 address \"%d\". Check power and connection.", + info->settings.node_addr); + return FE_ERR_HW; + } + + /* decode variables from buffer */ + pbuf = buffer; + info->node_vars[channel].control = *((unsigned char *)pbuf); // 0 + pbuf += sizeof(char); + DWORD_SWAP(pbuf); + info->node_vars[channel].u_demand = *((float *)pbuf); // 1 + pbuf += sizeof(float); + DWORD_SWAP(pbuf); + info->node_vars[channel].u_meas = *((float *)pbuf); // 2 + pbuf += sizeof(float); + DWORD_SWAP(pbuf); + info->node_vars[channel].i_meas = *((float *)pbuf); // 3 + pbuf += sizeof(float); + info->node_vars[channel].status = *((unsigned char *)pbuf); // 4 + pbuf += sizeof(char); + info->node_vars[channel].trip_cnt = *((unsigned char *)pbuf); // 5 + pbuf += sizeof(char); + DWORD_SWAP(pbuf); + info->node_vars[channel].ramp_up = *((float *)pbuf); // 6 + pbuf += sizeof(float); + DWORD_SWAP(pbuf); + info->node_vars[channel].ramp_down = *((float *)pbuf); // 7 + pbuf += sizeof(float); + DWORD_SWAP(pbuf); + info->node_vars[channel].u_limit = *((float *)pbuf); // 8 + pbuf += sizeof(float); + DWORD_SWAP(pbuf); + info->node_vars[channel].i_limit = *((float *)pbuf); // 9 + pbuf += sizeof(float); + DWORD_SWAP(pbuf); + info->node_vars[channel].ri_limit = *((float *)pbuf); // 10 + pbuf += sizeof(float); + info->node_vars[channel].trip_max = *((unsigned char *)pbuf); // 11 + pbuf += sizeof(char); + info->node_vars[channel].trip_time = *((unsigned char *)pbuf); // 12 + + /* mark voltage/current as valid in cache */ + info->node_vars[channel].cached = 1; + + return FE_SUCCESS; +} + +//---- device driver routines -------------------------------------- +/*! + *

Initializes the thermo element MSCB device driver, i.e. generates all the necessary + * structures in the ODB if necessary, and initializes the MSCB. Furthermore it makes + * some consitency checks to verify that the addressed module is indeed a thermo element + * MSCB card.

+ * + *

Return: + * - FE_SUCCESS if everything went smooth + * - FE_ERR_ODB otherwise + * + * \param hKey is the device driver handle given from the class driver + * \param pinfo is needed to store the internal info structure + * \param channels is the number of channels of the device (from the class driver) + */ +INT hvr800_init(HNDLE hKey, HVR800_INFO **pinfo, INT channels) +{ +INT status, size; +INT i; +HNDLE hDB, hkeydd; +MSCB_INFO node_info; +HVR800_INFO *info; + + // allocate info structure + info = (HVR800_INFO *) calloc(1, sizeof(HVR800_INFO)); + info->node_vars = (HVR800_NODE_VARS *) calloc(channels, sizeof(HVR800_NODE_VARS)); + *pinfo = info; + + cm_get_experiment_database(&hDB, NULL); + + // create HVR800 settings record + status = db_create_record(hDB, hKey, "DD/HVR800", HVR800_SETTINGS_STR); + if ((status != DB_SUCCESS) && (status != DB_OPEN_RECORD)) { + cm_msg(MERROR, "hvr800_init", "hvr800_init: Error creating DD/HVR800 record in ODB, status=%d", status); + cm_yield(0); + return FE_ERR_ODB; + } + + db_find_key(hDB, hKey, "DD/HVR800", &hkeydd); + size = sizeof(info->settings); + db_get_record(hDB, hkeydd, &info->settings, &size, 0); + + // initialize driver + info->errcount = 0; + info->startup_error = 0; + info->lasterrtime = ss_time(); + + if (!info->settings.enabled) { + cm_msg(MINFO, "hvr800_init", "hvr800_init: %s not enabled", info->settings.name); + cm_yield(0); + info->startup_error = 1; + return FE_SUCCESS; + } + + // initialize MSCB + info->fd = mscb_init(info->settings.port, sizeof(info->settings.port), info->settings.pwd, MSCB_DEBUG); + if (info->fd < 0) { + cm_msg(MINFO, "hvr800_init", "hvr800_init: Couldn't initialize MSCB port %s, Error no: %d", + info->settings.port, info->fd); + cm_yield(0); + info->startup_error = 1; + return FE_SUCCESS; + } + + // check first node + status = mscb_info(info->fd, info->settings.node_addr, &node_info); + if (status != MSCB_SUCCESS) { + cm_msg(MINFO, "hvr800_init", "Cannot access HVR node at address \"%d\". Please check cabling and power.", + info->settings.node_addr); + cm_yield(0); + info->startup_error = 1; + return FE_SUCCESS; + } + + // check if it is a HVR800 + if (strcmp(node_info.node_name, "HVR-800") != 0) { + cm_msg(MINFO, "hvr800_init", + "Found unexpected node \"%s\" at address \"%d\".", + node_info.node_name, info->settings.node_addr); + cm_yield(0); + info->startup_error = 1; + return FE_SUCCESS; + } + + // read all values from the HVR800 devices + for (i=0; istartup_error = 1; + return FE_SUCCESS; + } + } + +/* + // turn on the HV main switch + flag = HVR800_CTRL_HV_ON | HVR800_CTRL_REGULATION; + mscb_write_group(info->fd, 800, 0, &flag, 1); +*/ + + cm_msg(MINFO, "hvr800_init", "hvr800_init: %s initialized", info->settings.name); + cm_yield(0); + + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ +/*! + *

terminates the MSCB and free's the memory allocated for the DD info structure.

+ * + *

Return: FE_SUCCESS

+ * + * \param info is a pointer to the DD specific info structure + */ +INT hvr800_exit(HVR800_INFO *info) +{ + // call EXIT function of MSCB driver, usually closes device + if (!info->startup_error) + mscb_exit(info->fd); + + free(info->node_vars); + free(info); + + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ +/*! + *

set a high voltage value of a hvr_800 channel.

+ * + *

return: FE_SUCCESS

+ * + * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number + * \param value high voltage value in (kV) to be set + */ +INT hvr800_set(HVR800_INFO *info, INT channel, float value) +{ + INT status; + unsigned char hv_on_off; + float mscb_value; + + if ( info->startup_error ) { + ss_sleep(10); + return FE_SUCCESS; + } + + // set HV value + mscb_value = fabs(value); // V + status=mscb_write(info->fd, (unsigned short) (info->settings.node_addr+channel), + (unsigned char) HVR800_VDEMAND, &mscb_value, 4); + + // check if it is necessay to switch HV on/off + if (value == 0.0) { // demand HV==0 -> switch HV off + if ((info->node_vars[channel].control & HVR800_CTRL_HV_ON) != 0) { // HV still on, hence switch it off + hv_on_off = HVR800_CTRL_REGULATION; + status=mscb_write(info->fd, (unsigned short) (info->settings.node_addr+channel), + (unsigned char) HVR800_CONTROL, &hv_on_off, 1); + } + } else { + if ((info->node_vars[channel].control & HVR800_CTRL_HV_ON) == 0) { // HV has been off so far, hence switch it on + hv_on_off = HVR800_CTRL_HV_ON | HVR800_CTRL_REGULATION; + status=mscb_write(info->fd, (unsigned short) (info->settings.node_addr+channel), + (unsigned char) HVR800_CONTROL, &hv_on_off, 1); + } + } + + return FE_SUCCESS; +} + +/*-----------------------------------------------------------------------------*/ +/*! + *

get a high voltage value from a hvr_800 channel.

+ * + *

return: FE_SUCCESS

+ * + * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number + * \param pvalue read + */ +INT hvr800_get(HVR800_INFO *info, INT channel, float *pvalue) +{ +INT status, size; +DWORD nowtime, difftime; +unsigned char buffer[256], *pbuf; + + // error timeout facility + nowtime = ss_time(); + difftime = nowtime - info->lasterrtime; + if ( difftime > HVR800_DELTA_TIME_ERROR ) { + info->errcount = 0; + info->lasterrtime = ss_time(); + } + + // check if there was a startup error + if ( info->startup_error ) { + *pvalue = (float) HVR800_INIT_ERROR; + ss_sleep(10); // to keep CPU load low when Run active + return FE_SUCCESS; + } + + // check if the value was previously read by hvr800_read_all + if (info->node_vars[channel].cached) { + *pvalue = info->node_vars[channel].u_meas; + info->node_vars[channel].cached = 0; + return FE_SUCCESS; + } + + // read voltage and current at the same time + size = sizeof(buffer); + status = mscb_read_range(info->fd, info->settings.node_addr+channel, HVR800_CONTROL, HVR800_IMEAS, buffer, &size); + if (status != MSCB_SUCCESS) { + info->errcount++; + if (info->errcount < HVR800_MAX_ERROR) { + cm_msg(MINFO, "hvr800_get", + "Cannot access MSCB HVR address \"%d\". Check power and connection.", + info->settings.node_addr+channel); + return FE_SUCCESS; + } + } + + // decode variables from buffer + pbuf = buffer; + info->node_vars[channel].control = *((unsigned char *)pbuf); // 0 + pbuf += sizeof(char); + DWORD_SWAP(pbuf); + info->node_vars[channel].u_demand = *((float *)pbuf); // 1 + pbuf += sizeof(float); + DWORD_SWAP(pbuf); + info->node_vars[channel].u_meas = *((float *)pbuf); // 2 + pbuf += sizeof(float); + DWORD_SWAP(pbuf); + info->node_vars[channel].i_meas = *((float *)pbuf); // 3 + + + *pvalue = info->node_vars[channel].u_meas; + + + + ss_sleep(10); // to keep CPU load low + return FE_SUCCESS; +} + +//---------------------------------------------------------------------------- +/*! + *

sets the current limit of a channel of the hvr_800's. + * + *

return: FE_SUCCESS

+ * + * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number of the nhq + * \param limit is the current limit value + */ +INT hvr800_set_current_limit(HVR800_INFO *info, INT channel, float limit) +{ + INT status; + DWORD nowtime, difftime; + float mscb_value; + + if ( info->startup_error ) { + ss_sleep(10); + return FE_SUCCESS; + } + + // error timeout facility + nowtime = ss_time(); + difftime = nowtime - info->lasterrtime; + if ( difftime > HVR800_DELTA_TIME_ERROR ) { + info->errcount = 0; + info->lasterrtime = ss_time(); + } + + // set current limit value + mscb_value = limit; // uA + status=mscb_write(info->fd, (unsigned short) (info->settings.node_addr+channel), + (unsigned char) HVR800_ILIMIT, &mscb_value, 4); + + return FE_SUCCESS; +} + +//---------------------------------------------------------------------------- +/*! + *

sets the voltage limit of a channel of the hvr_800's. + * + *

return: FE_SUCCESS

+ * + * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number of the nhq + * \param limit is the voltage limit value + */ +INT hvr800_set_voltage_limit(HVR800_INFO *info, INT channel, float limit) +{ + INT status; + DWORD nowtime, difftime; + float mscb_value; + + if ( info->startup_error ) { + ss_sleep(10); + return FE_SUCCESS; + } + + // error timeout facility + nowtime = ss_time(); + difftime = nowtime - info->lasterrtime; + if ( difftime > HVR800_DELTA_TIME_ERROR ) { + info->errcount = 0; + info->lasterrtime = ss_time(); + } + + // set voltage limit value + mscb_value = limit; // V + status=mscb_write(info->fd, (unsigned short) (info->settings.node_addr+channel), + (unsigned char) HVR800_VLIMIT, &mscb_value, 4); + + return FE_SUCCESS; +} + +//---------------------------------------------------------------------------- +/*! + *

sets the ramping up speed in (kV/s) of a channel of the hvr_800's. + * If the value is "0", ramping is performed as fast as possible. + * + *

return: FE_SUCCESS

+ * + * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number of the nhq + * \param ramp is the ramping up value in (V/s) + */ +INT hvr800_set_rampup(HVR800_INFO *info, INT channel, float ramp) +{ + INT status; + DWORD nowtime, difftime; + float mscb_value; + + if ( info->startup_error ) { + ss_sleep(10); + return FE_SUCCESS; + } + + // error timeout facility + nowtime = ss_time(); + difftime = nowtime - info->lasterrtime; + if ( difftime > HVR800_DELTA_TIME_ERROR ) { + info->errcount = 0; + info->lasterrtime = ss_time(); + } + + // set ramp up value + mscb_value = ramp; // V/s + status=mscb_write(info->fd, (unsigned short) (info->settings.node_addr+channel), + (unsigned char) HVR800_RAMPUP, &mscb_value, 4); + + return FE_SUCCESS; +} + +//---------------------------------------------------------------------------- +/*! + *

sets the ramping down speed in (V/s) of a channel of the hvr_800's. + * If the value is "0", ramping is performed as fast as possible. + * + *

return: FE_SUCCESS

+ * + * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number of the nhq + * \param ramp is the ramping up value in (V/s) + */ +INT hvr800_set_rampdown(HVR800_INFO *info, INT channel, float ramp) +{ + INT status; + DWORD nowtime, difftime; + float mscb_value; + + if ( info->startup_error ) { + ss_sleep(10); + return FE_SUCCESS; + } + + // error timeout facility + nowtime = ss_time(); + difftime = nowtime - info->lasterrtime; + if ( difftime > HVR800_DELTA_TIME_ERROR ) { + info->errcount = 0; + info->lasterrtime = ss_time(); + } + + // set ramp down value + mscb_value = ramp; // V/s + status=mscb_write(info->fd, (unsigned short) (info->settings.node_addr+channel), + (unsigned char) HVR800_RAMPDOWN, &mscb_value, 4); + + return FE_SUCCESS; +} + +//---------------------------------------------------------------------------- +/*! + *

sets the trip time of a channel of the hvr_800's. + * + *

return: FE_SUCCESS

+ * + * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number of the nhq + * \param trip_time is the trip time + */ +INT hvr800_set_triptime(HVR800_INFO *info, INT channel, float trip_time) +{ + INT status; + DWORD nowtime, difftime; + unsigned char mscb_value; + + if ( info->startup_error ) { + ss_sleep(10); + return FE_SUCCESS; + } + + // error timeout facility + nowtime = ss_time(); + difftime = nowtime - info->lasterrtime; + if ( difftime > HVR800_DELTA_TIME_ERROR ) { + info->errcount = 0; + info->lasterrtime = ss_time(); + } + + // set trip time value + mscb_value = (unsigned char) trip_time; + status=mscb_write(info->fd, (unsigned short) (info->settings.node_addr+channel), + (unsigned char) HVR800_TRIPTIME, &mscb_value, 1); + + return FE_SUCCESS; +} + +//---------------------------------------------------------------------------- +//---- device driver entry point --------------------------------------------- +//---------------------------------------------------------------------------- +INT hvr800(INT cmd, ...) +{ +va_list argptr; +HNDLE hKey; +INT channel, status; +float value, *pvalue; +HVR800_INFO *info; + + va_start(argptr, cmd); + status = FE_SUCCESS; + + switch (cmd) { + case CMD_INIT: + { + hKey = va_arg(argptr, HNDLE); + HVR800_INFO **pinfo = va_arg(argptr, HVR800_INFO**); + channel = va_arg(argptr, INT); + status = hvr800_init(hKey, pinfo, channel); + } + break; + + case CMD_EXIT: + info = va_arg(argptr, HVR800_INFO *); + status = hvr800_exit(info); + break; + + case CMD_SET: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + status = hvr800_set(info, channel, value); + break; + + case CMD_GET: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + status = hvr800_get(info, channel, pvalue); + break; + + case CMD_GET_DEMAND: + case CMD_GET_DEMAND_DIRECT: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = info->node_vars[channel].u_demand; // V + break; + + case CMD_GET_CURRENT: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = info->node_vars[channel].i_meas; // uA + break; + + case CMD_GET_LABEL: + break; + + case CMD_SET_LABEL: + break; + + + case CMD_GET_STATUS: + //info = va_arg (argptr, void *); + //channel = (WORD) va_arg (argptr, INT); + //pstate = (DWORD *) va_arg (argptr, DWORD *); + //status = dd_device_chStatus_get (info, channel, pstate); + break; + + case CMD_SET_CURRENT_LIMIT: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + status = hvr800_set_current_limit(info, channel, value); + break; + + case CMD_SET_VOLTAGE_LIMIT: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + status = hvr800_set_voltage_limit(info, channel, value); + break; + + case CMD_GET_VOLTAGE_LIMIT: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float *); + *pvalue = info->node_vars[channel].u_limit; // V + break; + + case CMD_GET_THRESHOLD: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = 0.1f; + break; + + case CMD_GET_THRESHOLD_CURRENT: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = 0.001f; + break; + + case CMD_GET_THRESHOLD_ZERO: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = 0.02f; + break; + + case CMD_GET_CURRENT_LIMIT: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = info->node_vars[channel].i_limit; // uA + break; + + case CMD_SET_RAMPUP: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + hvr800_set_rampup(info, channel, value); + break; + + case CMD_SET_RAMPDOWN: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + hvr800_set_rampdown(info, channel, value); + break; + + case CMD_GET_RAMPUP: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = info->node_vars[channel].ramp_up; + break; + + case CMD_GET_RAMPDOWN: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = info->node_vars[channel].ramp_down; + break; + + case CMD_SET_TRIP_TIME: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + hvr800_set_triptime(info, channel, value); + break; + + case CMD_GET_TRIP_TIME: + info = va_arg(argptr, HVR800_INFO*); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + *pvalue = info->node_vars[channel].trip_time; + break; + + case CMD_STOP: + status = FE_SUCCESS; + break; + + default: + cm_msg(MERROR, "hvr800 device driver", "Received unkown command %d", cmd); + status = FE_ERR_DRIVER; + break; + } + + va_end(argptr); + + return status; +} + +//---------------------------------------------------------------------------- +// end ----------------------------------------------------------------------- +//---------------------------------------------------------------------------- diff --git a/device/hvr800.h b/device/hvr800.h new file mode 100644 index 0000000..3752638 --- /dev/null +++ b/device/hvr800.h @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------- + + Name: hvr800.h + Created by: Andreas Suter 2009/09/25 + + Contents: MIDAS device driver declaration for the MSCB high voltage + dividers hvr_500. + +---------------------------------------------------------------------*/ + +/*---- device driver declaration -----------------------------------*/ + +/*! + *

MIDAS device driver for the MSCB high voltage dividers hvr_800 + * (positive and negative). + */ +INT hvr800(INT cmd, ...); + diff --git a/frontend/hvr800_scfe.cxx b/frontend/hvr800_scfe.cxx new file mode 100644 index 0000000..0c4ac27 --- /dev/null +++ b/frontend/hvr800_scfe.cxx @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +#include "midas.h" +#include "mfe.h" + +#include "class/hv.h" +#include "device/hvr800.h" +#include "bus/null.h" + +//-- Globals ------------------------------------------------------- + +//! The frontend name (client name) as seen by other MIDAS clients +const char *frontend_name = "HVR800 high voltage divider"; + +//! The frontend file name, don't change it +const char *frontend_file_name = __FILE__; + +//! frontend_loop is called periodically if this variable is TRUE +BOOL frontend_call_loop = FALSE; //TODO: enable HV monitoring. + +//! a frontend status page is displayed with this frequency in ms +INT display_period = 1000; + +//! maximum event size produced by this frontend +INT max_event_size = 10000; + +//! maximum event size for fragmented events (EQ_FRAGMENTED) +INT max_event_size_frag = 5*1024*1024; + +//! buffer size to hold events +INT event_buffer_size = 10*10000; + +//! device driver list +DEVICE_DRIVER hv_driver[] = { + { "HVR800_1", hvr800, 8, null, DF_PRIO_DEVICE | DF_HW_RAMP | DF_REPORT_STATUS}, + { "HVR800_2", hvr800, 8, null, DF_PRIO_DEVICE | DF_HW_RAMP | DF_REPORT_STATUS}, + { "HVR800_3", hvr800, 8, null, DF_PRIO_DEVICE | DF_HW_RAMP | DF_REPORT_STATUS}, + { "HVR800_4", hvr800, 8, null, DF_PRIO_DEVICE | DF_HW_RAMP | DF_REPORT_STATUS}, + { "HVR800_5", hvr800, 8, null, DF_PRIO_DEVICE | DF_HW_RAMP | DF_REPORT_STATUS}, + { ""},//List needs to be terminated by an empty name. Otherwise, unallocated memory is accessed during initialization! +}; + +/*! + * equipment_common_overwrite: + * + * - If that flag is TRUE, then the contents of the "equipment" structure is copied to + * the ODB on each start of the front-end. + * + * - If the flag is FALSE, then the ODB values are kept on the start of the front-end + */ +BOOL equipment_common_overwrite = FALSE; + +//! equipment structure for the mfe.c +EQUIPMENT equipment[] = { + + { "HV Detectors", // equipment name + {203, 0, // event ID, trigger mask + "SYSTEM", // event buffer + EQ_SLOW, // equipment type + 0, // event source + "FIXED", // format + TRUE, // enabled + RO_RUNNING | + RO_TRANSITIONS, // read when running and on transitions + 30000, // read every 30 sec + 0, // stop run after this event limit + 0, // number of sub events + 1, // log history every event + "", "", "",}, + cd_hv_read, // readout routine + cd_hv, // class driver main routine + hv_driver, // device driver list + NULL, // init string + }, + + { "" } +}; + +INT poll_event(INT source, INT count, BOOL test) {return CM_SUCCESS;}; + +INT interrupt_configure(INT cmd, INT source, POINTER_T adr) {return CM_SUCCESS;}; + +INT frontend_init() +{ + // de-register run-transition notifications + cm_deregister_transition(TR_START); + cm_deregister_transition(TR_STOP); + cm_deregister_transition(TR_PAUSE); + cm_deregister_transition(TR_RESUME); + + return CM_SUCCESS; +} + +INT frontend_exit() +{ + return CM_SUCCESS; +} + +INT frontend_loop() +{ + return CM_SUCCESS; +} + +INT begin_of_run(INT run_number, char *error) +{ + return CM_SUCCESS; +} + +INT end_of_run(INT run_number, char *error) +{ + return CM_SUCCESS; +} + +INT pause_run(INT run_number, char *error) +{ + return CM_SUCCESS; +} + +INT resume_run(INT run_number, char *error) +{ + return CM_SUCCESS; +} \ No newline at end of file