Initial working commit

This commit is contained in:
2025-11-04 16:17:39 +01:00
commit 4e2d16afb1
7 changed files with 1374 additions and 0 deletions
+246
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
---
+1
View File
@@ -0,0 +1 @@
build
+111
View File
@@ -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
# <TARGET_FILE:frontend_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 $<TARGET_FILE:hvr800_fe> -e ${MIDAS_EXPTAB_NAME} || true
COMMENT "Initial run of frontend to register it in the ODB"
)
+69
View File
@@ -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 <directory-to-build-in> -DBUILD_FRONTEND=ON
cmake --build <directory-to-build-in> --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(
<path-to-hvr800-repository>
)
add_executable(
<frontend-name>
<frontend-code-path-1>
<frontend-code-path-2>
:
)
set_property(
TARGET
<frontend-name>
PROPERTY
CXX_STANDARD 11
)
target_include_directories(
<frontend-name>
PRIVATE
$ENV{MIDASSYS}/drivers
$ENV{MIDASSYS}/include
)
target_link_libraries(
<frontend-name>
hvr800
<additional-library-1>
<additional-library-2>
:
)
```
+804
View File
@@ -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 <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <cstring>
#include <cmath>
#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
/*!
* <p>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\
"
//------------------------------------------------------------------
/*!
* <p>Variable set of a hvr800 channel.
*
* <p>The control byte can be or'ed with the following values:
* - 0x0 HV off
* - 0x1 HV on
* - 0x2 HV regulated
* - 0x4 HV idle
*
* <p>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 -------------------------------------------------
/*!
* <p>
*
* \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 --------------------------------------
/*!
* <p>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.</p>
*
* <p><b>Return:</b>
* - 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; i<channels; i++) {
status = hvr800_read_all(info, i);
if (status != FE_SUCCESS) {
info->startup_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;
}
/*----------------------------------------------------------------------------*/
/*!
* <p>terminates the MSCB and free's the memory allocated for the DD info structure.</p>
*
* <p><b>Return:</b> FE_SUCCESS</p>
*
* \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;
}
/*----------------------------------------------------------------------------*/
/*!
* <p>set a high voltage value of a hvr_800 channel.</p>
*
* <p><b>return:</b> FE_SUCCESS</p>
*
* \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;
}
/*-----------------------------------------------------------------------------*/
/*!
* <p>get a high voltage value from a hvr_800 channel.</p>
*
* <p><b>return:</b> FE_SUCCESS</p>
*
* \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;
}
//----------------------------------------------------------------------------
/*!
* <p>sets the current limit of a channel of the hvr_800's.
*
* <p><b>return:</b> FE_SUCCESS</p>
*
* \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;
}
//----------------------------------------------------------------------------
/*!
* <p>sets the voltage limit of a channel of the hvr_800's.
*
* <p><b>return:</b> FE_SUCCESS</p>
*
* \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;
}
//----------------------------------------------------------------------------
/*!
* <p>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.
*
* <p><b>return:</b> FE_SUCCESS</p>
*
* \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;
}
//----------------------------------------------------------------------------
/*!
* <p>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.
*
* <p><b>return:</b> FE_SUCCESS</p>
*
* \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;
}
//----------------------------------------------------------------------------
/*!
* <p>sets the trip time of a channel of the hvr_800's.
*
* <p><b>return:</b> FE_SUCCESS</p>
*
* \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 -----------------------------------------------------------------------
//----------------------------------------------------------------------------
+18
View File
@@ -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 -----------------------------------*/
/*!
* <p>MIDAS device driver for the MSCB high voltage dividers hvr_800
* (positive and negative).
*/
INT hvr800(INT cmd, ...);
+125
View File
@@ -0,0 +1,125 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#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;
}