From 2966a33233761dc2dff10e7bc0c09b8af99b3388 Mon Sep 17 00:00:00 2001 From: smathis Date: Fri, 24 Jul 2026 15:24:08 +0200 Subject: [PATCH] Initial compiling version of the bronkhorst driver --- .clang-format | 246 +++ .gitea/workflows/action.yaml | 18 + .gitignore | 1 + CMakeLists.txt | 135 ++ README.md | 69 + device/bronkhorst_flowmeter.cxx | 2791 ++++++++++++++++++++++++++ device/bronkhorst_flowmeter.h | 21 + frontend/bronkhorst_flowmeter_fe.cxx | 164 ++ 8 files changed, 3445 insertions(+) create mode 100644 .clang-format create mode 100644 .gitea/workflows/action.yaml create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 device/bronkhorst_flowmeter.cxx create mode 100644 device/bronkhorst_flowmeter.h create mode 100644 frontend/bronkhorst_flowmeter_fe.cxx 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/.gitea/workflows/action.yaml b/.gitea/workflows/action.yaml new file mode 100644 index 0000000..cd615e3 --- /dev/null +++ b/.gitea/workflows/action.yaml @@ -0,0 +1,18 @@ +name: Test And Build +on: [push] + +jobs: + Format: + runs-on: linepics + steps: + - name: checkout repo + uses: actions/checkout@v4 + - name: formatting + run: clang-format --style=file --Werror --dry-run device/* frontends/* + Lint: + runs-on: linepics + steps: + - name: checkout repo + uses: actions/checkout@v4 + - name: cppcheck + run: cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 device/ frontends/ 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..7c8b1b8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,135 @@ +cmake_minimum_required(VERSION 3.03) + +project(keller_dv2ps 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(CMAKE_PREFIX_PATH "$ENV{MIDASSYS}") +find_package(Midas REQUIRED) + +set( + TCPIP_DRIVER_DIR + $ENV{MIDASSYS}/drivers/bus/tcpip.cxx + CACHE STRING + "path to tcpip driver that should be used" +) + +set( + DRIVERS + $ENV{MIDASSYS}/drivers/class/multi.cxx +) + +set(SYS_LIBS + pthread + util + rt + dl +) + +set(MIDAS_LIBS + midas::mfe + midas::midas + midas::mscb +) + +set(LIBS ${SYS_LIBS} ${MIDAS_LIBS}) + +################################################################################ +## make tcpip library configurable +################################################################################ + +if(IS_DIRECTORY ${TCPIP_DRIVER_DIR}) + add_subdirectory( + # TODO not sure, should it be a submodule? does it really make sense to + # separate everything? + ${TCPIP_DRIVER_DIR} ./bus/tcpip + ) + + set( + LIBS + ${LIBS} + tcpip + ) +else() + set( + DRIVERS + ${DRIVERS} + ${TCPIP_DRIVER_DIR} + ) +endif() + +################################################################################ +## Device Library +################################################################################ + +add_library( + bronkhorst_flowmeter + device/bronkhorst_flowmeter.cxx + ${DRIVERS} +) + +set_property( + TARGET + bronkhorst_flowmeter + PROPERTY + CXX_STANDARD 11 +) + +target_include_directories( + bronkhorst_flowmeter + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + $ + $ + PRIVATE + $ENV{MIDASSYS}/drivers +) + +target_link_libraries( + bronkhorst_flowmeter + ${LIBS} +) + +################################################################################ +## Test Frontend +################################################################################ + +if(${BUILD_FRONTEND}) + + add_executable( + bronkhorst_flowmeter_fe + frontend/bronkhorst_flowmeter_fe.cxx + ) + + set_property( + TARGET + bronkhorst_flowmeter_fe + PROPERTY + CXX_STANDARD 11 + ) + + target_include_directories( + bronkhorst_flowmeter_fe + PRIVATE + $ENV{MIDASSYS}/drivers + ) + + target_link_libraries( + bronkhorst_flowmeter_fe + bronkhorst_flowmeter + ) + +endif() diff --git a/README.md b/README.md new file mode 100644 index 0000000..998c7e9 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# Bronkhorst gas flowmeter / flow controller + +This repository contains the driver code and Midas frontend for the Bronkhorst +gas flowmeter / flow controller. + +## 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/bronkhorst_flowmeter_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 bronkhorst_flowmeter repository is outside of the frontend +# repository, you also need to provide a location for the bronkhorst_flowmeter +# artifacts as a second argument to `add_subdirectory` +# e.g. `./device/bronkhorst_flowmeter` +add_subdirectory( + +) + +add_executable( + + + + : +) + +set_property( + TARGET + + PROPERTY + CXX_STANDARD 11 +) + +target_include_directories( + + PRIVATE + $ENV{MIDASSYS}/drivers + $ENV{MIDASSYS}/include +) + +target_link_libraries( + + bronkhorst_flowmeter + + + : +) +``` diff --git a/device/bronkhorst_flowmeter.cxx b/device/bronkhorst_flowmeter.cxx new file mode 100644 index 0000000..8184ab1 --- /dev/null +++ b/device/bronkhorst_flowmeter.cxx @@ -0,0 +1,2791 @@ +/********************************************************************\ + + Name: bronkhorst.cxx + Created by: Andreas Suter 2003/09/12 + Modified by: RA36 to be able to set Helium flow in liter/minutes + and to have a flow limit. + This was suggested by SR35. + + Contents: device driver for Bronkhorst He gas flowcontroller, + defined as a multi class device. + + RS232: 38400 baud, 8 data bits, 1 stop bit, no parity bit, + protocol: no flow control, termination \r\n (CR\LF) + + device driver: Naming of the input and output channels must be done in + "../Settings/Names Input" and "../Settings/Names Ouput". + + The channel names keywords defined in the device driver settings + key ("../Settings/Device/in/DD") + for Input and .../out/DD for + Output should not be changed! They are used to assign the + Bronkhorst command to the Midas channel for commands and queries. + If more than one Bronkhorst flow controller is being used, + the prefix may be changed from BH to e.g: BH0 and BH1 + * Input in DD +[32] BH Flow (l/min)\n\ +[32] BH FlowDAC\n\ +[32] BH Flow_SP (readback l/min)\n\ +[32] BH FlowDAC_SP (readback)\n\ +[32] BH ControlMode (readback)\n\ +[32] BH Temperature (C)\n\ +[32] BH Valve Position (rdbck 0..1)\n\ + * Output in DD +[32] BH FlowSP\n\ +[32] BH ValvePosSP_TST\n\ + + * without Flow l/min +[0] BH Flow Measured +[1] BH Valve Position (rdbck 0..1) +[2] BH Flow Setpoint (readback) +[3] BH Control Mode (readback) +[4] BH Temperature(C) + +[0] BH Flow Setpoint +[1] BH Valve Position Setpoint TST + + "FlowDAC_SP", "Flow_SP", "FlowSP" or "Flow Setpoint" are keywords for + BH_SETPOINT 9 + "FlowDAC" or "Flow" are keywords for BH_MEASURE 8 + "Temperature" is the keyword for BH_TEMPERATURE 142 + "Valve Position" or "ValvePosSP" BH_VALVEOUT 55 + "ControlMode" or "Control Mode" BH_CNTRLMODE 12 + + Date Name Modification + ----------------------------------------------------------------- + 16-APR-2025 RA36 Modified disconnect/reconnect and added watchdog + +\********************************************************************/ + +#include +#include +#include +#include + +#include "midas.h" +//#include "mfe.h" +#include "msystem.h" +#include "math.h" // for rintf + +#include "bronkhorst_flowmeter.h" + +#ifndef TCPIP_RECONNECT +#define TCPIP_RECONNECT +#endif + +/*---- when TCPIP_RECONNECT is #defined before tcpip.h is included the tcpip bus driver + * structure is defined to be able to reset tcp/ip communication --------*/ +#include "bus/tcpip.h" + +#ifdef HAVE_ETS +#include "ets_logout.h" +#endif +#ifdef HAVE_EDS +#include "eds_logout.h" +#endif + +#ifdef SCW_USED +#include "lemSCWatchdog.h" +#endif + +/*---- extern globals from mfe.c -----------------------------------*/ + +extern INT run_number; +extern INT run_state; + +/*---- globals -----------------------------------------------------*/ + +#ifndef SCW_USED // when LEM slow control watchdog is not used +#define WATCHDOG_USED // disable checking of this front end by watchdog during +#endif // communication to EL-FLOW or during reconnection attempts + +//! timeout in (ms) for the communication between pc and bronkhorst +#define BH_TIME_OUT 2000 + +//! timeout in (sec) between logout terminal server an reconnection trial +#define BH_RECONNECTION_TIMEOUT 5 +//! maximum number of readback failures before a reconnect will take place +#define BH_MAX_READBACK_FAILURE 5 +//! maximum number of reconnections before bailing off +#define BH_MAX_RECONNECTION_FAILURE 5 + +/* --------- to handle error messages ------------------------------*/ +#define BH_MAX_ERROR 10 //!< maximum number of error messages +#define BH_DELTA_TIME_ERROR 3600 //!< reset error counter after BH_DELTA_TIME_ERROR seconds + +#define BH_WAIT 500 //!< time (ms) to BH_WAIT between commands + +#define BH_CMD_ERROR -3 //!< unvalid command error +#define BH_INIT_ERROR -2 //!< initialize error tag +#define BH_READ_ERROR -1 //!< read error tag + +//! sleep time (us) between the telnet commands of the ets_logout +#define BH_ETS_LOGOUT_SLEEP 10000 +//! sleep time (us) between the telnet commands of the eds_logout +#define BH_EDS_LOGOUT_SLEEP 10000 + +//#define MIDEBUG // General +//#define MIDEBUG1 // +//#define MIDEBUG2 // +//#define MIDEBUGF1 // function calls +//#define MIDEBUGF2 // function calls +//#define MIDEBUGF3 // _in() and _out() + +/* --------- Bronkhorst stuff --------------------------------------*/ + +/*! + * bronkhorst <-> midas tags. + * def's starting with BH_ aren't documented here. For a detailed + * description of them see the bronkhorst manual 'RS232 INTERFACE + * with FLOW-BUS protocol' Doc. no.: 9.17.027 D + */ +#define BH_SUCCESS 1 + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +#define BH_CMD_UNKNOWN 2 +#define BH_COMM_ERR 3 +#define BH_STATUS_ERR 4 +#define BH_DECODE_ERR 5 +#define BH_CMD_PARAM 6 + +// bronkhorst commands See Parameter number (DDE) in Parameter properties table +#define BH_NO_CMD -1 +#define BH_IDENTSTRNG 1 // R Identification string e.g.: SNM22214346B reply is null terminated string (length = -2) +#define BH_MEASURE 8 // R Measure (measured value 100% = 32000) named Flow DAC +#define BH_SETPOINT 9 // RW Setpoint (wanted value 100% = 32000) named Flow DAC SP +#define BH_SETSLOPE 10 +#define BH_CNTRLMODE 12 +#define BH_POLYCNSTA 13 +#define BH_POLYCNSTB 14 +#define BH_POLYCNSTC 15 +#define BH_POLYCNSTD 16 +#define BH_POLYCNSTE 17 +#define BH_POLYCNSTF 18 +#define BH_POLYCNSTG 19 +#define BH_POLYCNSTH 20 +#define BH_CAPACITY 21 +#define BH_SENSORTYPE 22 // Sensor type e.g.: 3 (3= gas volume) +#define BH_CAPUNIT 23 +#define BH_FLUIDNR 24 +#define BH_FLUIDNAME 25 // Fluid name e.g.: He +#define BH_ALARMINFO 28 +#define BH_ALARMMSGTA 33 +#define BH_ALARMMSGNR 34 +#define BH_VALVEOUT 55 // RW Valve output (valve output signal) scaled 0..1 +#define BH_VALVEOFFSET 59 // Valve offset e.g.: 0 +#define BH_IOSTATUS 86 // IOS Status e.g.: 15 +#define BH_DEVICETYPE 90 // Device type e.g.: DMFC +#define BH_MODELNUM 91 // BHTModel number e.g.: F-111AC-50K-ABD-92-V for 50 l/min Helium + // F-201CV-10 for 19 l/min Helium + // F-201CV-5K for 9 l/min Helium + // 00000000011 + // 12345678901 +#define BH_SERIALNUM 92 // Serial number e.g.: M22214346B +#define BH_VERSION 105 // Firmware version e.g.: V1.26e +#define BH_TEMPERATURE 142 // Temperature [degC] e.g.: 22.559591 +#define BH_CALIBVOLLT 145 // Calibrated volume in liter +#define BH_PRESSURE 143 // Not present? Command not understood +#define BH_CONTRFEAT 166 // Controller features +#define BH_PID_KP 167 // PID factor Kp +#define BH_PID_TI 168 // PID factor Ti +#define BH_PID_TD 169 // PID factor Td +#define BH_FLUIDTEMP 181 // Not present? Returns always -10.000000 +// etc, rest still missing + +// bronkhorst flowbus commands, addresses +#define BH_STATUSMSG 0 +#define BH_SETPARAM 1 +#define BH_GETPARAM 4 + +#define BH_CHAR 0x00 +#define BH_INTEGER 0x20 +#define BH_LONG 0x40 +#define BH_FLOAT 0x40 +#define BH_STRING 0x60 + +// bronkhorst status and error messages +#define BH_NOERROR 0x00 +#define BH_PROCESS_CLAIMED 0x01 +#define BH_CMD_ERR 0x02 +#define BH_PROC_ERR 0x03 +// etc, rest still missing + +// bronkhorst node +#define BH_NODE 0x80 + +// bronkhorst chained +#define BH_UNCHAINED 0x00 +#define BH_CHAINED 0x80 + +// bronkhorst max needle valve value = 2^24 - 1 +#define BH_MAX_NEEDLE_VALVE 16777215 + +#define BH_IN_CHANNELS 7 //!< number of input channels +#define BH_OUT_CHANNELS 2 //!< number of output channels + +// NOTE: if these lines are commented the driver returns to Flow DAC implementation +// in addition BH_IN_CHANNELS, BH_IN_SETTINGS structure and const char *bh_in_settings_str = +// have to be edited to eliminate l/min input channels and settings +#define bronkhorst_flowmeter_inCHANINDEX 0 //!< index of Flow readout (l/min) +#define BH_SETPOINT_INCHANINDEX 2 //!< index of Flow Setpoint readout (l/min) +#define BH_SETPOINT_OUTCHANINDEX 0 //!< index of Flow Setpoint (l/min) + +#define BH_VALVEPOS_INCHANINDEX 6 //!< index of Valve Position readout 0..1 +#define BH_VALVEPOS_OUTCHANINDEX 1 //!< index of Valve Position setpoint 0..1 + +#endif // DOXYGEN_SHOULD_SKIP_THIS + +/*! + *

generally it stores internal informations within the DD. In this + * case this are the names for the ODB describing the in-channels. + */ +typedef struct { + float Flow_lpm_Max; + float Flow_lpm_Offset; + float Flow_lpm_to_DAC_Scale; + INT FlowDAC_Max; + INT FlowDAC_Offset; + char SetOutofRangeSetpoint_to_Max; + char reset_to_defaults;; + + INT detailed_msg; //!< flag indicating if detailed status/error messages are wanted +#ifdef HAVE_ETS + INT ets_in_use; //!< flag indicating if the rs232 terminal server is in use +#endif +#ifdef HAVE_EDS + INT eds_in_use; //!< flag indicating if the rs232 terminal server is in use +#endif +#ifdef SCW_USED + INT scw_in_use; //!< flag indicating if the slowcontrol watchdog shall be used +#endif + char name[BH_IN_CHANNELS][32]; //!< input channel names +} BH_IN_SETTINGS; + +/*! + *

generally it stores internal informations within the DD. In this + * case this are the names for the ODB describing the out-channels. + */ +typedef struct { + char name[BH_OUT_CHANNELS][32]; //!< output channel names +} BH_OUT_SETTINGS; + +//! initializing string for the BH_IN_SETTINGS structure. +const char *bh_in_settings_str = + +#ifdef HAVE_ETS // ---------------------------------------- + +// 1234567890123456789012345678901 +#ifdef SCW_USED +"Flow lpmin Maximum = FLOAT : 50.0 \n\ +Flow lpmin Offset = FLOAT : 0.0 \n\ +Flow lpmin to DAC Scale = FLOAT : 640.0 \n\ +Flow DAC Maximum = INT : 32000 \n\ +Flow DAC Offset = INT : 0 \n\ +Set out of Range FlowSP to Max = CHAR : N \n\ +Reset to Defaults of Type = CHAR : Y \n\ +Detailed Messages = INT : 0\n\ +ETS_IN_USE = INT : 1\n\ +SCW_IN_USE = INT : 0\n\ +Input = STRING[7]:\n\ +[32] BH Flow (l/min)\n\ +[32] BH FlowDAC\n\ +[32] BH Flow_SP (readback l/min)\n\ +[32] BH FlowDAC_SP (readback)\n\ +[32] BH ControlMode (readback)\n\ +[32] BH Temperature (C)\n\ +[32] BH Valve Position (rdbck 0..1)\n\ +"; +#else +"Flow lpmin Maximum = FLOAT : 50.0 \n\ +Flow lpmin Offset = FLOAT : 0.0 \n\ +Flow lpmin to DAC Scale = FLOAT : 640.0 \n\ +Flow DAC Maximum = INT : 32000 \n\ +Flow DAC Offset = INT : 0 \n\ +Set out of Range FlowSP to Max = CHAR : N \n\ +Reset to Defaults of Type = CHAR : Y \n\ +Detailed Messages = INT : 0\n\ +Detailed Messages = INT : 0\n\ +ETS_IN_USE = INT : 1\n\ +Input = STRING[7]:\n\ +[32] BH Flow (l/min)\n\ +[32] BH FlowDAC\n\ +[32] BH Flow_SP (readback l/min)\n\ +[32] BH FlowDAC_SP (readback)\n\ +[32] BH ControlMode (readback)\n\ +[32] BH Temperature (C)\n\ +[32] BH Valve Position (rdbck 0..1)\n\ +"; +#endif + +#elif defined( HAVE_EDS ) //------------------------------- + +#ifdef SCW_USED +"Flow lpmin Maximum = FLOAT : 50.0 \n\ +Flow lpmin Offset = FLOAT : 0.0 \n\ +Flow lpmin to DAC Scale = FLOAT : 640.0 \n\ +Flow DAC Maximum = INT : 32000 \n\ +Flow DAC Offset = INT : 0 \n\ +Set out of Range FlowSP to Max = CHAR : N \n\ +Reset to Defaults of Type = CHAR : Y \n\ +Detailed Messages = INT : 0\n\ +Detailed Messages = INT : 0\n\ +EDS_IN_USE = INT : 1\n\ +SCW_IN_USE = INT : 0\n\ +Input = STRING[7]:\n\ +"; +#else +"Flow lpmin Maximum = FLOAT : 50.0 \n\ +Flow lpmin Offset = FLOAT : 0.0 \n\ +Flow lpmin to DAC Scale = FLOAT : 640.0 \n\ +Flow DAC Maximum = INT : 32000 \n\ +Flow DAC Offset = INT : 0 \n\ +Set out of Range FlowSP to Max = CHAR : N \n\ +Reset to Defaults of Type = CHAR : Y \n\ +Detailed Messages = INT : 0\n\ +Detailed Messages = INT : 0\n\ +EDS_IN_USE = INT : 1\n\ +Input = STRING[7]:\n\ +[32] BH Flow (l/min)\n\ +[32] BH FlowDAC\n\ +[32] BH Flow_SP (readback l/min)\n\ +[32] BH FlowDAC_SP (readback)\n\ +[32] BH ControlMode (readback)\n\ +[32] BH Temperature (C)\n\ +[32] BH Valve Position (rdbck 0..1)\n\ +"; +#endif + +#else // ---- NOT HAVE_ETS AND NOT HAVE_EDS -------------- + +#ifdef SCW_USED +"Flow lpmin Maximum = FLOAT : 50.0 \n\ +Flow lpmin Offset = FLOAT : 0.0 \n\ +Flow lpmin to DAC Scale = FLOAT : 640.0 \n\ +Flow DAC Maximum = INT : 32000 \n\ +Flow DAC Offset = INT : 0 \n\ +Set out of Range Flow to Max = CHAR : N \n\ +Reset to Defaults of Type = CHAR : Y \n\ +Detailed Messages = INT : 0\n\ +Detailed Messages = INT : 0\n\ +SCW_IN_USE = INT : 0\n\ +Input = STRING[7]:\n\ +[32] BH Flow (l/min)\n\ +[32] BH FlowDAC\n\ +[32] BH Flow_SP (readback l/min)\n\ +[32] BH FlowDAC_SP (readback)\n\ +[32] BH ControlMode (readback)\n\ +[32] BH Temperature (C)\n\ +[32] BH Valve Position (rdbck 0..1)\n\ +"; +#else +"Flow lpmin Maximum = FLOAT : 50.0 \n\ +Flow lpmin Offset = FLOAT : 0.0 \n\ +Flow lpmin to DAC Scale = FLOAT : 640.0 \n\ +Flow DAC Maximum = INT : 32000 \n\ +Flow DAC Offset = INT : 0 \n\ +Set out of Range Flow to Max = CHAR : N \n\ +Reset to Defaults of Type = CHAR : Y \n\ +Detailed Messages = INT : 0\n\ +Detailed Messages = INT : 0\n\ +Input = STRING[7]:\n\ +[32] BH Flow (l/min)\n\ +[32] BH FlowDAC\n\ +[32] BH Flow_SP (readback l/min)\n\ +[32] BH FlowDAC_SP (readback)\n\ +[32] BH ControlMode (readback)\n\ +[32] BH Temperature (C)\n\ +[32] BH Valve Position (rdbck 0..1)\n\ +"; +#endif +#endif // ------------------------------------------------- + +//! initializing string for the BH_OUT_SETTINGS structure. +// 1234567890123456789012345678901 +#ifdef BH_SETPOINT_OUTCHANINDEX //!< Flow Setpoint (l/min) +const char *bh_out_settings_str = +"Output = STRING[2] :\n\ +[32] BH FlowSP\n\ +[32] BH ValvePosSP_TST\n\ +"; +#else +const char *bh_out_settings_str = +"Output = STRING[2] :\n\ +[32] BH Flow Setpoint\n\ +[32] BH ValvePosSP_TST\n\ +"; +#endif + +//! defines the slowcontrol default watchdog info structure +const char *bh_scw_str = +"Proc Name = STRING : [32]\n\ +PID = INT : -1\n\ +Log Name = STRING : [64]\n\ +DD Name = STRING : [32] bronkhorst\n\ +Last Updated = DWORD : 0\n\ +Timeout = DWORD : 180\n\ +"; + +/*! + *

This structure contains private variables for the device driver. + */ +typedef struct { + BH_IN_SETTINGS bh_in_settings; //!< internal DD settings for the in-channels + BH_OUT_SETTINGS bh_out_settings; //!< internal DD settings for the out-channels +#ifdef SCW_USED + LEM_SC_WATCHDOG scw; //!< slowcontrol watchdog info structure +#endif + HNDLE hkey; //!< ODB key for bus driver info + INT num_channels_in; //!< number of in-channels + INT num_channels_out; //!< number of out-channels + INT (*bd)(INT cmd, ...); //!< bus driver entry function + void *bd_info; //!< private info of bus driver + int bd_connected; //!< flag showing if bus driver is connected + int first_bd_error; //!< flag showing if the bus driver error message + // is already given + INT errorcount; //!< error coutner + INT startup_error; //!< startup error tag; if set, the get and bh_set + // and bh_get routines wont do anything + DWORD lasterrtime; //!< timer for error handling + INT readback_failure; //!< counts the number of readback failures + float last_valid_value[BH_IN_CHANNELS];//!< stores the last valid value of the Input channel + float last_set[BH_OUT_CHANNELS]; //!< stores the last valid value of the Output channel + float last_setval[BH_OUT_CHANNELS]; //!< stores the last valid value of the Output channel + // sent to the controller + DWORD last_reconnect; //!< timer for bus driver reconnect error handling + int reconnection_failures; //!< how often reconnection failed + INT startup_tag; //!< tag indicating that the DD is starting + // (see bh_in_init and BH_out_init) + HNDLE keyOut; //!< HNDLE to /Equipment//Variables/Output + HNDLE keyDDin; //!< HNDLE to BHFCin/DD + char name[NAME_LENGTH]; //!< equipment + float volume; //!< volume ln/min deduced from BHTModel number +} BH_INFO; + +BH_INFO *bh_info; //!< global info structure since port (rs232 or terminal server) + // can only be initialized once. + +typedef INT(func_t) (INT cmd, ...); + +/*---- support routines ------------------------------------------------------------*/ + +INT bh_get(BH_INFO *, INT , float *); +INT bh_set(BH_INFO *, INT , float ); +INT bh_show_param(BH_INFO *info); + +float bh_afdiff(float , float); + +/*----------------------------------------------------------------------------*/ +float bh_afdiff(float a, float b) { // return absolute difference of a-b + float diff; + + diff = a-b; + if (diff < 0.0) diff = -diff; + + return diff; +} +/*----------------------------------------------------------------------------*/ +INT fatal_error_message(void) { + cm_msg(MERROR, "BH", "**** FATAL SET-UP ERROR! FIX ERROR IN DEVICE DRIVER ODB " + "AND RESTART FRONTEND! ****"); + cm_msg_flush_buffer(); + return CM_SUCCESS; +} + +/*----------------------------------------------------------------------------*/ + +/*! + *

Remove trailing blanks (and carriage control characters) from char buf

+ */ +INT remove_trailing_blanks(char *buf) { + INT len; + + len = 0; + if (buf) { + len = strlen(buf); + while (len > 0) { + if (isspace(*(buf+len-1))) { + *(buf+(--len)) = '\0'; + } else + break; + } + } + return len; +} + +/*----------------------------------------------------------------------------------*/ +/*! + *

decodes for any error messages coming from the bronkhorst flow meter.

+ *

return error tag: + * - BH_SUCCESS = no error + * - BH_STATUS_ERR = bronkhost status error + *

+ * \param str return string from the flowmeter + * \param error_msg error message return string (if error), otherwise empty string. + */ +int bh_success(char *str, char *error_msg) +{ + int error = 0; + int error_byte = 0; + +#ifdef MIDEBUGF2 + cm_msg(MLOG,"",">>bh_success(%s)", str); +#endif + strcpy(error_msg, ""); // initialize error message + + // data link error message HOST -> PC ":01" + if (strncmp(str, ":01",3)==0) { + + if (sscanf(str, ":01%02d", &error) == 1) { + switch (error) { + case 1: // ":0101" +#ifdef OLDMBC + strcpy(error_msg, "BH ERROR :0101 no ':' at the start of the message."); +#else + strcpy(error_msg, "BH ERROR :0101 general error."); +#endif + break; + case 2: // ":0102" +#ifdef OLDMBC + strcpy(error_msg, "BH ERROR :0102 error in 1st byte."); +#else + strcpy(error_msg, "BH ERROR :0102 general error."); +#endif + break; + case 3: // ":0103" +#ifdef OLDMBC + strcpy(error_msg, "BH ERROR :0103 error in 2nd byte or number of bytes is 0 " + "or message too long."); +#else + strcpy(error_msg, "BH ERROR :0103 propar protocol error."); +#endif + break; + case 4: // ":0104" +#ifdef OLDMBC + strcpy(error_msg, "BH ERROR :0104 error in received message (receiver overrun" + ", framing error, etc.)."); +#else + strcpy(error_msg, "BH ERROR :0104 propar protocol error (or CRC error)."); +#endif + break; + case 5: // ":0105" +#ifdef OLDMBC + strcpy(error_msg, "BH ERROR :0105 flowbus communication BH ERROR: timeout or " + "message rejected by receiver."); +#else + strcpy(error_msg, "BH ERROR :0105 general error."); +#endif + break; + case 8: +#ifdef OLDMBC + strcpy(error_msg, "BH ERROR :0108 timeout during sending."); +#else + strcpy(error_msg, "BH ERROR :0108 general error."); +#endif + break; + case 9: +#ifdef OLDMBC + strcpy(error_msg, "BH ERROR :0109 no answer received within timeout."); +#else + strcpy(error_msg, "BH ERROR :0109 reponse message timeout."); +#endif + break; + + default: + snprintf(error_msg, 128, "BH ERROR :01%2.2d unknown error, obscure.", error); + break; + } // end switch + + } else { + snprintf(error_msg, 128, "BH ERROR \"%s\" may not be read by sscanf()", str); + } // end if (sscanf()) + + } else if (strncmp(str, ":04", 3) == 0) { // status error messages + + // :048000 + // : and 04 (message length) 80 (default node 80hex = 128 dec) 00 (Command status) + if (sscanf(str, ":048000%02d%02d", &error, &error_byte) == 2) { + switch (error) { + + case 0x00: // 00 : No error + break; + + case 0x01: + sprintf(error_msg, "BH ERROR 01 : Process claimed. Claimed process = %x", + error_byte); + break; + + case 0x02: + sprintf(error_msg, "BH ERROR 02 : Command error. Error_byte = %d", + error_byte); + break; + + case 0x03: + sprintf(error_msg, "BH ERROR 03 : Process error. Error_byte = %d", + error_byte); + break; + + case 0x04: + sprintf(error_msg, "BH ERROR 04 : Parameter error. Error_byte = %d", + error_byte); + break; + + case 0x05: + sprintf(error_msg, "BH ERROR 05 : Parameter type error. Error_byte = %d", + error_byte); + break; + + case 0x06: + sprintf(error_msg, "BH ERROR 06 : Parameter value error. Error_byte = %d", + error_byte); + break; + + case 0x07: + sprintf(error_msg, "BH ERROR 07 : Network not active. Error_byte = %d", + error_byte); + break; + + case 0x08: + sprintf(error_msg, "BH ERROR 08 : Time-out start character. Error_byte = %d", + error_byte); + break; + + case 0x09: + sprintf(error_msg, "BH ERROR 09 : Time-out serial line. Error_byte = %d", + error_byte); + break; + + case 0x0A: + sprintf(error_msg, "BH ERROR 0A : Hardware memory error. Error_byte = %d", + error_byte); + break; + + case 0x0B: + sprintf(error_msg, "BH ERROR 0B : Node number error. Error_byte = %d", + error_byte); + break; + + case 0x0C: + sprintf(error_msg, "BH ERROR 0C : General communication error. " + "Error_byte = %d", error_byte); + break; + + case 0x0D: + sprintf(error_msg, "BH ERROR 0D : Read only parameter. Error_byte = %d", + error_byte); + break; + + case 0x0E: + sprintf(error_msg, "BH ERROR 0E : Error PC-communication. Error_byte = %d", + error_byte); + break; + + case 0x0F: + sprintf(error_msg, "BH ERROR 0F : No RS232 connection. Error_byte = %d", + error_byte); + break; + + case 0x10: + sprintf(error_msg, "BH ERROR 10 : PC out of memory. Error_byte = %d", + error_byte); + break; + + case 0x11: + sprintf(error_msg, "BH ERROR 11 : Write only parameter. Error_byte = %d", + error_byte); + break; + + case 0x12: + sprintf(error_msg, "BH ERROR 12 : System configuration unknown. " + "Error_byte = %d", error_byte); + break; + + case 0x13: + sprintf(error_msg, "BH ERROR 13 : No free node address. Error_byte = %d", + error_byte); + break; + + case 0x14: + sprintf(error_msg, "BH ERROR 14 : Wrong interface type. Error_byte = %d", + error_byte); + break; + + case 0x15: + sprintf(error_msg, "BH ERROR 15 : Error serial port connection. " + "Error_byte = %d", error_byte); + break; + + case 0x16: + sprintf(error_msg, "BH ERROR 16 : Error opening communication. " + "Error_byte = %d", error_byte); + break; + + case 0x17: + sprintf(error_msg, "BH ERROR 17 : Communication error. Error_byte = %d", + error_byte); + break; + + case 0x18: + sprintf(error_msg, "BH ERROR 18 : Error interface bus master. " + "Error_byte = %d", error_byte); + break; + + case 0x19: + sprintf(error_msg, "BH ERROR 19 : Timeout answer. Error_byte = %d", + error_byte); + break; + + case 0x1A: + sprintf(error_msg, "BH ERROR 1A : No start character. Error_byte = %d", + error_byte); + break; + + case 0x1B: + sprintf(error_msg, "BH ERROR 1B : Error first digit. Error_byte = %d", + error_byte); + break; + + case 0x1C: + sprintf(error_msg, "BH ERROR 1C : Buffer overflow in host. Error_byte = %d", + error_byte); + break; + + case 0x1D: + sprintf(error_msg, "BH ERROR 1D : Buffer overflow. Error_byte = %d", + error_byte); + break; + + case 0x1E: + sprintf(error_msg, "BH ERROR 1E : No answer found. Error_byte = %d", + error_byte); + break; + + case 0x1F: + sprintf(error_msg, "BH ERROR 1F : Error closing communication. " + "Error_byte = %d", error_byte); + break; + + case 0x20: + sprintf(error_msg, "BH ERROR 20 : Synchronization error. Error_byte = %d", + error_byte); + break; + + case 0x21: + sprintf(error_msg, "BH ERROR 21 : Send error. Error_byte = %d", error_byte); + break; + + case 0x22: + sprintf(error_msg, "BH ERROR 22 : Protocol error. Error_byte = %d", + error_byte); + break; + + case 0x23: + sprintf(error_msg, "BH ERROR 23 : Buffer overflow in module. " + "Error_byte = %d", error_byte); + break; + + default: + sprintf(error_msg, "BH ERROR: Unknown error %2.2d, obscure. " + "Error_byte = %d", error, error_byte); + break; + } // end switch + + } else { + snprintf(error_msg, 128, "BH ERROR \"%s\" may not be read by sscanf()", str); + } // end if (sscanf()) + + } // end if + + if (strlen(error_msg)>0) { +#ifdef MIDEBUGF2 + cm_msg(MLOG,"","< if the return message of the flowcontroller is a number, this routine + * is used to decode the return string into a number.

+ *

Return: + * - BH_SUCCESS if everthing went fine + * - BH_DECODE_ERR otherwise + *

+ * \param rply return string from flowcontroller + * \param decode_tag BH_CHAR, BH_INTEGER, BH_FLOAT, BH_LONG + * \param err_msg error message if something went wrong + * \param result decoded value + */ +int bh_decode_number(char *rply, int decode_tag, char *err_msg, float *result) +{ + int totlen, len; + int ivalue; + char str[128]; + union { + int ival; + float fval; + } hexfloat; + + totlen = strlen(rply); + if (totlen == 0) { // no input string + strcpy(err_msg, "BH ERROR: no string to decode ..."); + return BH_DECODE_ERR; // ----->>>>>>>>>>>>>! + } + + switch (decode_tag) { + case BH_CHAR: + case BH_INTEGER: + sscanf(rply, ":%02x%s", &len, str); + if (totlen != 2*len+5) { + strcpy(err_msg, "BH ERROR: wrong string length ..."); + return BH_DECODE_ERR; // ----->>>>>>>>>>>>>! + } + sscanf(rply, ":%10x%x", &len, &ivalue); + *result = (float)ivalue; + break; + case BH_FLOAT: + /* case BH_LONG: // identical value as BH_FLOAT therefore omitted */ + sscanf(rply, ":%02x%s", &len, str); + if (totlen != 2*len+5) { + strcpy(err_msg, "BH ERROR: wrong string length ..."); + return BH_DECODE_ERR; // ----->>>>>>>>>>>>>! + } + sscanf(rply, ":%10x%x", &len, &(hexfloat.ival)); + *result = hexfloat.fval; + break; + + default: + break; + } + return BH_SUCCESS; +} + +/*----------------------------------------------------------------------------------*/ +/*! + *

if the return message of the flowcontroller is a string, this routine + * is used to decode the return string.

+ *

Return: + * - BH_SUCCESS if everthing went fine + * - BH_DECODE_ERR otherwise + *

+ * \param rply return string from flowcontroller to be decoded + * \param err_msg error message if something went wrong + * \param result decoded message + */ +int bh_decode_string(char *rply, char *err_msg, char *result) +{ + int totlen, len; + int ivalue; + char str[128]; + + totlen = strlen(rply); + if (totlen == 0) { // no string to decode + strcpy(err_msg, "BH ERROR: no string to decode ..."); + return BH_DECODE_ERR; // ----->>>>>>>>>>>>>! + } + + sscanf(rply, ":%02x%s", &len, str); + if (totlen != 2*len+5) { + strcpy(err_msg, "BH ERROR: wrong string length ..."); + return BH_DECODE_ERR; // ----->>>>>>>>>>>>>! + } + sscanf(rply, ":%10x%02x%s", &ivalue, &len, str); + hex2ascii(str, len, result); + + return BH_SUCCESS; +} + +/*----------------------------------------------------------------------------------*/ +/*! + *

routine which is used to read back values from the flowcontroller.

+ *

Return: + * - BH_SUCCESS if everything went fine + * - BH_COMM_ERR, BH_CMD_UNKNOWN or BH_CMD_PARAM otherwise + * \param info info structure used by BD_PUTS and BD_GETS macros + * \param cmd command tag telling which value to read back from the flowcontroller. + * \param result return string from the flowcontroller. + */ +INT bh_get_send_rcv(BH_INFO *info, INT cmd, char *result) +{ + char str[128], err_msg[128], send[128]; + int status; + int cmd_length, process, parameter, decode_tag, str_length; +#ifdef WATCHDOG_USED + BOOL wflag; + DWORD wtimeout; +#endif + + status = -1; + + // RA36 10-JUL-2023 init receive buffer + if (result != NULL) *result = '\0'; + + if ((info == NULL) || (info->bd_info == NULL)) { +#ifdef MIDEBUG1 + cm_msg(MLOG, "", "bh_get_send_rcv((NULL pointer to BusDriver info) Returning -1"); + cm_msg_flush_buffer(); +#endif + return BH_COMM_ERR; // ----->>>>>>>>>>>>>! + } + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"",">>bh_get_send_rcv(cmd=%d)", cmd); +#endif + + cmd_length = 6; // default for requests except when returning strings + process = 0; parameter = 0; decode_tag = -1; str_length = 0; + + // filter command and prepare command string for device + switch (cmd) { + + case BH_IDENTSTRNG:// 1 = Identification string (process 0, parameter 0) + // 12 char string returned + // reply is a null terminated string (length = -2) max. length is unknown. 30 is the length which does not return error 17. + cmd_length = 7; process = 0; parameter = 0; decode_tag = BH_STRING; str_length = 30; // was 12 (-2) Null terminated + break; + + case BH_MEASURE: // 8 = Measure (process <> 0?, parameter 0) + // integer (2 byte int) returned according to manual 32000 is 100% + process = 1; parameter = 0; decode_tag = BH_INTEGER; + break; + + case BH_SETPOINT: // 9 = Setpoint (process <> 0?, parameter 1) + // long(4 byte int) returned according to manual 32000 is 100% + process = 1; parameter = 1; decode_tag = BH_INTEGER; + break; + + case BH_CNTRLMODE: // 12 = Controlmode (process <> 0?, parameter 4) 1 byte returned + process = 1; parameter = 4; decode_tag = BH_CHAR; + break; + + case BH_SENSORTYPE: // 22 + process = 1; parameter = 14; decode_tag = BH_CHAR; + break; + + case BH_FLUIDNAME: // 25 + cmd_length = 7; process = 1; parameter = 17; decode_tag = BH_STRING; str_length = 10; + break; + + case BH_VALVEOUT: // 55 = Valve output (process 114, parameter 1) + // according to manual valve output signal is a 24-bit number + // in the range 0..14.3Vdc/0..23.3Vdc + process = 114; parameter = 1; decode_tag = BH_LONG; + break; + + case BH_VALVEOFFSET: // 59 + process = 116; parameter = 7; decode_tag = BH_INTEGER; + break; + + case BH_IOSTATUS: // 86 = IO status (process 114, parameter 11) 1 byte returned + process = 114; parameter = 11; decode_tag = BH_CHAR; + break; + + case BH_DEVICETYPE: // 90 + cmd_length=7; process = 113; parameter = 1; decode_tag = BH_STRING; str_length = 6; + break; + + case BH_MODELNUM: // 91 + // reply is a null terminated string (length = -2) max. length is unknown. 30 is the length which does not return error 17. + cmd_length=7; process = 113; parameter = 2; decode_tag = BH_STRING; str_length = 30; // was 10 (-2) Null terminated + break; + + case BH_SERIALNUM: // 92 + // reply is a null terminated string (length = -2) max. length is unknown. 30 is the length which does not return error 17. + cmd_length=7; process = 113; parameter = 3; decode_tag = BH_STRING; str_length = 30; // was 10 (-2) Null terminated + break; + + case BH_VERSION: // 105 + cmd_length=7; process = 113; parameter = 5; decode_tag = BH_STRING; str_length = 6; + break; + + case BH_TEMPERATURE: // 142 + process = 33; parameter = 7; decode_tag = BH_FLOAT; + break; + + case BH_CALIBVOLLT: // 145 + process = 33; parameter = 10; decode_tag = BH_FLOAT; + break; + + case BH_PRESSURE: // 143 + process = 33; parameter = 8; decode_tag = BH_FLOAT; + break; + + case BH_CONTRFEAT: // 166 + process = 114; parameter = 20; decode_tag = BH_CHAR; + break; + + case BH_PID_KP: // 167 + process = 114; parameter = 21; decode_tag = BH_FLOAT; + break; + + case BH_PID_TI: // 168 + process = 114; parameter = 22; decode_tag = BH_FLOAT; + break; + + case BH_PID_TD: // 169 + process = 114; parameter = 23; decode_tag = BH_FLOAT; + break; + + case BH_FLUIDTEMP: // 181 + process = 113; parameter = 16; decode_tag = BH_FLOAT; + break; + + default: + cm_msg(MLOG,"","bh_get_send_rcv() Unknown command %d", cmd); + return BH_CMD_UNKNOWN; // ----->>>>>>>>>>>>>! + break; + } + +#ifdef MIDEBUG2 + cm_msg(MLOG,"","cmd_length=%d, process=%d, decode_tag=%d, parameter=%d, str_length=%d", + cmd_length, process, decode_tag, parameter, str_length); +#endif + + // generate command for string request + if (decode_tag == BH_STRING) { + + if ((cmd_length == 7) && (str_length > 0)) { + // for BH_IDENSTRING :078004006000600C + // : 07 80 04 00 60 00 60 0C + sprintf(str, ":%02X%02X%02X%02X%02X%02X%02X%02X", + cmd_length, BH_NODE, BH_GETPARAM, BH_UNCHAINED|process, + BH_UNCHAINED|decode_tag, process, decode_tag|parameter, str_length); + } else { + if (cmd_length != 7) + cm_msg(MLOG,"","bh_get_send_rcv() Command %d : Unexpected cmd_length %d " + "instead of 7 for string", cmd, cmd_length); + if (str_length <= 0) + cm_msg(MLOG,"","bh_get_send_rcv() Command %d : str_length %d to return " + "string <= 0", cmd, str_length); + return BH_CMD_PARAM; // ----->>>>>>>>>>>>>! + } + + // generate command for all other requests + } else if (cmd_length == 6) { + + if (decode_tag >= 0) { + sprintf(str, ":%02X%02X%02X%02X%02X%02X%02X", + cmd_length, BH_NODE, BH_GETPARAM, BH_UNCHAINED|process, + BH_UNCHAINED|decode_tag, process, decode_tag|parameter); + } else { + cm_msg(MLOG,"","bh_get_send_rcv() Command %d : Unexpected or undefined (= -1) decode_tag %d", cmd, + decode_tag); + return BH_CMD_PARAM; // ----->>>>>>>>>>>>>! + } + + } else { + cm_msg(MLOG,"","bh_get_send_rcv() Command %d : Unexpected cmd_length %d", cmd, + cmd_length); + return BH_CMD_PARAM; // ----->>>>>>>>>>>>>! + } + + strcpy(send,str); + +#ifdef MIDEBUG2 + cm_msg(MLOG, "", "Command to query is _%s_", str); +#endif + + // append carriage return at the end + strcat(str,"\r\n"); + +#ifdef WATCHDOG_USED + cm_get_watchdog_params(&wflag, &wtimeout); // remember watchdog parameters + cm_set_watchdog_params(FALSE,0); // disable watchdog while + // accessing device to learn how to handle network problems +#endif + + // send command to device + BD_PUTS(str); + // read back response from device + str[0] = '\0'; + status = BD_GETS(str, sizeof(str), "\r\n", BH_TIME_OUT); + strcpy(result, str); // cr/lf should be returned + remove_trailing_blanks(str); // remove space (cr,lf) for cm_msg, only + + +#ifdef WATCHDOG_USED + cm_set_watchdog_params(wflag, wtimeout); // restore watchdog parameters +#endif + + if (status <= 0) { // no response or no connection + if ( info->errorcount < BH_MAX_ERROR ) { + if (info->bh_in_settings.detailed_msg) + cm_msg(MERROR, "bh_get_send_rcv", "Communication error with bronkhorst " + "flowcontroller."); + info->errorcount++; + } + return BH_COMM_ERR; // ----->>>>>>>>>>>>>! + } + + // check for errors + if (bh_success(str, err_msg) != BH_SUCCESS) { + if ( info->errorcount < BH_MAX_ERROR ) { + if (info->bh_in_settings.detailed_msg) { + cm_msg(MLOG,"","Send: \"%s\", Answer: \"%s\"", send, str); + cm_msg(MERROR, "bh_get_send_rcv", "cmd=%d : %s", cmd, err_msg); + } + info->errorcount++; + } + return BH_COMM_ERR; // ----->>>>>>>>>>>>>! +#ifdef MIDEBUGF1 + } else { + cm_msg(MINFO, "", "<routine which is used to set values of the flowcontroller.

+ *

Return: + * - BH_SUCCESS if everthing went fine + * - BH_COMM_ERR if there was a communication problem + * - BH_CMD_UNKNOWN if the command is unkown + * \param info info structure used by BD_PUTS and BD_GETS macros + * \param cmd command tag telling which value to read back from the flowcontroller. + * \param value value to be set + */ +INT bh_set_send_rcv(BH_INFO *info, INT cmd, float value) +{ + char str[128], err_msg[128]; + int status; + int cmd_length, process, parameter, decode_tag; +#ifdef WATCHDOG_USED + BOOL wflag; + DWORD wtimeout; +#endif + + status = -1; + + if ((info == NULL) || (info->bd_info == NULL)) { +#ifdef MIDEBUG1 + cm_msg(MLOG, "", "bh_set_send_rcv((NULL pointer to BusDriver info) Returning -1"); + cm_msg_flush_buffer(); +#endif + return BH_COMM_ERR; // ----->>>>>>>>>>>>>! + } + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"",">>bh_set_send_rcv(cmd=%d, value=%f)", cmd, value); +#endif + + decode_tag = 0; + + // filter command and generate command string for device + switch (cmd) { + + case BH_SETPOINT: // 9 + if ((value >= 0) && (value <= 32767)) { // if value < 0 or > 32767 do nothing!! + cmd_length = 6; + process = 1; + parameter = 1; + decode_tag = BH_INTEGER; + sprintf(str, ":%02X%02X%02X%02X%02X%04X\r\n", + cmd_length, BH_NODE, BH_SETPARAM, BH_UNCHAINED|process, + BH_UNCHAINED|decode_tag|parameter, (int)value); + } else { + // NIY if out of range value was not set before -> write message + cm_msg(MLOG,"","Flow Setpoint NOT SET! Value %d to be set is out of range | 0 - 32767 |", + (int)value); + return BH_SUCCESS; // ----->>>>>>>>>>>>>! + } + break; + + case BH_VALVEOUT: // 55 + if ((value >= 0.f) && (value <= 1.f)) { // if value < 0 or > 1 do nothing!! + cmd_length = 8; + process = 114; + parameter = 1; + decode_tag = BH_LONG; + sprintf(str, ":%02X%02X%02X%02X%02X%08X\r\n", + cmd_length, BH_NODE, BH_SETPARAM, BH_UNCHAINED|process, + BH_UNCHAINED|BH_LONG|parameter, (int)(value*BH_MAX_NEEDLE_VALVE)); + } else { + // NIY if out of range value was not set before -> write message + cm_msg(MLOG,"","Valve Position Setpoint: Value %.3f to be set is out of range | 0 - 1 |", value); + return BH_SUCCESS; // ----->>>>>>>>>>>>>! + } + break; + + default: + return BH_CMD_UNKNOWN; // ----->>>>>>>>>>>>>! + break; + } + +#ifdef WATCHDOG_USED + cm_get_watchdog_params(&wflag, &wtimeout); // remember watchdog parameters + cm_set_watchdog_params(FALSE,0); // disable watchdog while + // accessing device to learn how to handle network problems +#endif + + // send command to device + BD_PUTS(str); + + // read back response from device + str[0] = '\0'; + status = BD_GETS(str, sizeof(str), "\r\n", BH_TIME_OUT); + remove_trailing_blanks(str); // eliminate carriage control for cm_msg + + +#ifdef WATCHDOG_USED + cm_set_watchdog_params(wflag, wtimeout); // restore watchdog parameters +#endif + + if (status <= 0) { // no response + if ( info->errorcount < BH_MAX_ERROR ) { + if (info->bh_in_settings.detailed_msg) + cm_msg(MERROR, "bh_set_send_rcv", "Communication error with bronkhorst " + "flowcontroller."); + info->errorcount++; + } +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<>>>>>>>>>>>>! + } + + // decode return string and check for errors + if (bh_success(str, err_msg) != BH_SUCCESS) { + if ( info->errorcount < BH_MAX_ERROR ) { + if (info->bh_in_settings.detailed_msg) + cm_msg(MERROR, "bh_set_send_rcv", "%s", err_msg); + info->errorcount++; + } +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<>>>>>>>>>>>>! + } + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<routine which filters the channel name and generates the decode command tag

+ *

Return: decode command tag

+ * \param str channel name + * \param decode_tag decode tag needed for the decode routines + * bh_set_send_rcv, bh_get_send_rcv + */ +INT bh_get_cmd(char *str, int *decode_tag) +{ + int cmd; + +#ifdef MIDEBUGF2 + cm_msg(MLOG,"",">>bh_get_cmd(%s)", str); +#endif + + cmd=BH_NO_CMD; +/* Input in DD +[32] BH Flow (l/min)\n\ +[32] BH FlowDAC\n\ +[32] BH Flow_SP (readback l/min)\n\ +[32] BH FlowDAC_SP (readback)\n\ +[32] BH ControlMode (readback)\n\ +[32] BH Temperature (C)\n\ +[32] BH Valve Position (rdbck 0..1)\n\ + * Output in DD +[32] BH FlowSP\n\ +[32] BH ValvePosSP_TST\n\ + + // without Flow l/min +[0] BH Flow Measured +[1] BH Valve Position (rdbck 0..1) +[2] BH Flow Setpoint (readback) +[3] BH Control Mode (readback) +[4] BH Temperature(C) + +[0] BH Flow Setpoint +[1] BH Valve Position Setpoint TST + */ + + if (strstr(str,"FlowDAC_SP")||strstr(str,"Flow_SP")||strstr(str,"FlowSP")|| + strstr(str,"Flow Setpoint")) { + cmd = BH_SETPOINT; // 9 + *decode_tag = BH_INTEGER; + } else if (strstr(str,"FlowDAC")||strstr(str,"Flow")) { + cmd = BH_MEASURE; // 8 + *decode_tag = BH_INTEGER; + } + if (strstr(str,"Temperature")) { + cmd = BH_TEMPERATURE; // 142 + *decode_tag = BH_FLOAT; + } + if (strstr(str, "Valve Position")||strstr(str, "ValvePosSP")) { + cmd = BH_VALVEOUT; // 55 + *decode_tag = BH_INTEGER; + } + if (strstr(str,"ControlMode")||strstr(str,"Control Mode")) { + cmd = BH_CNTRLMODE; // 12 + *decode_tag = BH_CHAR; + } + +#ifdef MIDEBUGF2 + cm_msg(MLOG,"","<Initializes the bronkhorst device driver, i.e. generates all the necessary + * structures in the ODB if necessary, initializes the bus driver and the bronkhorst + * mass flow meter.

+ *

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) + * \param bd is a pointer to the bus driver + */ +INT bh_in_init(HNDLE hKey, BH_INFO **pinfo, INT channels, func_t *bd) +{ + INT status, size, chno; + INT cmd; + HNDLE hDB, hkeydd; + char tpath[MAX_ODB_PATH]; + char bh_id[128], result[128], err_msg[128]; + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"",">>bh_in_init(channels=%d)",channels); +#endif + + size = 0; + cm_get_experiment_database(&hDB, NULL); + + // allocate info structure + BH_INFO *info = (BH_INFO*)calloc(1, sizeof(BH_INFO)); + if (pinfo) *pinfo = info; + if (info == NULL) { + cm_msg(MERROR,"bh_in_init","NULL pointer to BH_INFO"); + fatal_error_message(); + return FE_ERR_DRIVER; // ----->>>>>>>>>>>>>! + } + bh_info = info; // keep global pointer + + info->volume = 0.0; + + if ((status = db_get_path(hDB, hKey, tpath, sizeof(tpath))) == DB_SUCCESS) { + int i; + for (i = strlen("/Equipment/"); i < strlen(tpath); i++) { + if (tpath[i] == '/') { + tpath[i] = '\0'; + strncpy(info->name, &tpath[strlen("/Equipment/")], NAME_LENGTH); + info->name[NAME_LENGTH - 1] = '\0'; + break; + } + } + #ifdef MIDEBUGF1 + cm_msg(MINFO, "bh_in_init", " Equipment name is %s", info->name); + #endif + } else { + cm_msg(MERROR, "bh_in_init", "ERROR %d getting key path", status); + } + cm_msg_flush_buffer(); + + // create DD settings record +//status = db_create_record(hDB, hKey, "DD/BH", bh_in_settings_str); + status = db_create_record(hDB, hKey, "DD", bh_in_settings_str); + if ((status != DB_SUCCESS) && (status != DB_OPEN_RECORD)) { + cm_msg(MERROR, "bh_in_init", "bh_in_init: Couldn't create DD/BH in ODB: status=%d", + status); + fatal_error_message(); + return FE_ERR_ODB; // ----->>>>>>>>>>>>>! + } + + // create BH slowcontrol watchdog DD settings +#ifdef SCW_USED + status = db_create_record(hDB, hKey, "DD/SCW", bh_scw_str); + if ((status != DB_SUCCESS) && (status != DB_OPEN_RECORD)) { + cm_msg(MERROR, "bh_in_init", "bh_in_init: Couldn't create DD/SCW in ODB: status=%d", + status); + cm_msg_flush_buffer(); + return FE_ERR_ODB; // ----->>>>>>>>>>>>>! + } +#endif + + // copy DD entries into the info structure +//db_find_key(hDB, hKey, "DD/BH", &hkeydd); + db_find_key(hDB, hKey, "DD", &hkeydd); + db_open_record(hDB, hkeydd, &info->bh_in_settings, sizeof(info->bh_in_settings), + MODE_READ, NULL, NULL); + + /* initialize driver */ + info->hkey = hKey; + info->keyDDin = hkeydd; + info->num_channels_in = channels; + info->bd = bd; + info->errorcount = 0; + info->lasterrtime = ss_time(); + info->startup_error = 0; + info->readback_failure = 0; + info->last_reconnect = ss_time(); + info->reconnection_failures = 0; + info->first_bd_error = 1; + info->startup_tag = 0; + + if ( info->num_channels_in > BH_IN_CHANNELS ) { + chno = BH_IN_CHANNELS; + cm_msg(MERROR,"BH_in_init", "Error, max. number of BH input channels is %d, not %d.", + chno, info->num_channels_in); + cm_msg_flush_buffer(); + info->startup_error = 1; + fatal_error_message(); + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + + if (!bd) { + fatal_error_message(); + return FE_ERR_DRIVER; // ----->>>>>>>>>>>>>! + } + + /* initialize bus driver */ + status = info->bd(CMD_INIT, hKey, &info->bd_info); + if (status != FE_SUCCESS) { + info->startup_error = 1; + cm_msg(MLOG,"","info->bd(CMD_INIT,...) returned status %d", status); + cm_msg_flush_buffer(); + fatal_error_message(); + return status; // ----->>>>>>>>>>>>>! + } + info->bd_connected = TRUE; + + /* initialize BH */ + cmd = BH_IDENTSTRNG; + status = bh_get_send_rcv(info, cmd, result); + + if ( status != BH_SUCCESS ) { // error occurred + cm_msg(MERROR,"BH_in_init", "Error getting device query from BH, %s", + info->bh_in_settings.name[0]); + cm_msg_flush_buffer(); + info->startup_error = 1; + fatal_error_message(); + return FE_ERR_HW; // ----->>>>>>>>>>>>>! + } + + if (bh_decode_string(result, err_msg, bh_id) != BH_SUCCESS) { + cm_msg(MERROR,"BH_in_init", "%s", err_msg); + cm_msg_flush_buffer(); + info->startup_error = 1; + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + + // NOTE first character is 0x07 = ^G or \a control character for Bell. + // This is currently not filtered but it does not seem to harm the message log. + // e.g: SNM22214346 + cm_msg(MINFO,"BH_in_init", "Device query of BH returns Identification string %s", bh_id); + bh_show_param(info); + cm_msg_flush_buffer(); + +#ifdef SCW_USED + if (info->bh_in_settings.scw_in_use) { // slowcontrol watchdog shall be used + // register with the slowcontrol watchdog ------------------------------------- + // find scw record in DD + db_find_key(hDB, hKey, "DD/SCW", &hkeydd); + // get record + size = sizeof(info->scw); + db_get_record(hDB, hkeydd, &info->scw, &size, 0); + // fill watchdog structure + info->scw.pid = ss_getpid(); + info->scw.last_updated = ss_time(); + // write info back into ODB + db_set_record(hDB, hkeydd, &info->scw, sizeof(info->scw), 0); + + // register with slowcontrol watchdog + status = lem_scw_init(&info->scw); + if (status != LEM_SCW_SUCCESS) { + cm_msg(MINFO, "bh_in_init", "Couldn't register with lem watchdog"); + cm_yield(0); + } + } +#endif /* #ifdef SCW_USED */ + + result[0] = '\0'; + info->volume = -1.0; + status = bh_get_send_rcv(info, BH_MODELNUM, result); + if (status == BH_SUCCESS) { + char model[128]; + + // string was returned + model[0] = '\0'; + status = bh_decode_string(result, err_msg, model); + + if (status == BH_SUCCESS) { +#ifdef MIDEBUG1 + cm_msg(MLOG,"","DDE Parameter %d : %s = %s", BH_MODELNUM, "BHTModel number", model); +#endif + if (strncmp(model,"F-111AC-50K",11) == 0) { + info->volume = 50.0; + } else if (strncmp(model,"F-201CV-10",10) == 0) { + info->volume = 19.0; + } else if (strncmp(model,"F-201CV-5K",10) == 0) { + info->volume = 9.0; + } else { + cm_msg(MERROR,"bh_in_init","WARNING BHTModel number \"%s\" is not known! " + "Check currently set DD parameter and adapt them when they " + "are not correct! Add model case here", model); + info->volume = -4.0; + } + } else { + cm_msg(MERROR,"bh_in_init","cmd=%d, para_name=\"BHTModel number\": bh_decode_string() " + "returned : %s", BH_MODELNUM, err_msg); + info->volume = -3.0; + } + } else { + cm_msg(MERROR,"bh_in_init","cmd=%d, para_name=\"BHTModel number\": bh_get_send_rcv() " + "returned : %d", BH_MODELNUM, status); + info->volume = -2.0; + } + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<Initializes the bronkhorst device driver, i.e. generates all the necessary + * structures in the ODB if necessary, no initialization of the bus driver etc. + * is done, since this is part of BH_in_init.

+ *

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) + * \param bd is a pointer to the bus driver + */ +INT bh_out_init(HNDLE hKey, BH_INFO **pinfo, INT channels, func_t *bd) +{ + INT status, chno; + HNDLE hDB, hkeydd; + BH_INFO *info; + char odb_output[MAX_ODB_PATH]; + float dummy; + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"",">>bh_out_init(channels=%d)",channels); +#endif + + cm_get_experiment_database(&hDB, NULL); + + info = bh_info; + if (pinfo) *pinfo = info; + + if (bh_info == NULL) { + cm_msg(MERROR,"bh_out_init","NULL pointer to BH_INFO allocated in " + "bh_in_init()"); + fatal_error_message(); + return FE_ERR_DRIVER; // ----->>>>>>>>>>>>>! + } + + /* create DD settings record */ + status = db_create_record(hDB, hKey, "DD", bh_out_settings_str); + if ((status != DB_SUCCESS) && (status != DB_OPEN_RECORD)) { + cm_msg(MERROR, "bh_out_init", "Couldn't create DD: status=%d", status); + return FE_ERR_ODB; // ----->>>>>>>>>>>>>! + } + + // copy DD entries into the info structure + db_find_key(hDB, hKey, "DD", &hkeydd); + db_open_record(hDB, hkeydd, &info->bh_out_settings, sizeof(info->bh_out_settings), + MODE_READ, NULL, NULL); + + /* initialize driver */ + info->num_channels_out = channels; + + if ( info->num_channels_out != BH_OUT_CHANNELS ) { + chno = BH_OUT_CHANNELS; + cm_msg(MERROR,"bh_out_init", "Error, allowed number of BH output channels is %d, " + "not %d.", chno, info->num_channels_out); + info->startup_error = 1; + return FE_ERR_HW; // ----->>>>>>>>>>>>>! + } + + sprintf(odb_output, "/Equipment/%s/Variables/Output",info->name); + status = db_find_key(hDB, 0, odb_output, &info->keyOut); + if (status != FE_SUCCESS) { // couldn't get the output key maybe it is not there yet + info->keyOut = 0; // but we got another chance + } else { + + // get and then set Flow Setpoint in the ODB to readback value of Flow Setpoint +#ifdef BH_SETPOINT_INCHANINDEX + bh_get(info, BH_SETPOINT_INCHANINDEX, &dummy); +#ifdef BH_SETPOINT_OUTCHANINDEX + // initialize + info->last_set[BH_SETPOINT_OUTCHANINDEX] = dummy; + info->last_setval[BH_SETPOINT_OUTCHANINDEX] = info->last_valid_value[BH_SETPOINT_INCHANINDEX+1]; + // update ODB + db_set_data_index(hDB, info->keyOut, &dummy, sizeof(float),BH_SETPOINT_OUTCHANINDEX,TID_FLOAT); +#endif /* #ifdef BH_SETPOINT_OUTCHANINDEX */ +#endif /* #ifdef BH_SETPOINT_INCHANINDEX */ + + // get and set the Valve Position (to make reconnect smoother when already initialized) +#ifdef BH_VALVEPOS_INCHANINDEX + bh_get(info, BH_VALVEPOS_INCHANINDEX, &dummy); +#ifdef BH_VALVEPOS_OUTCHANINDEX + db_set_data_index(hDB, info->keyOut, &dummy, sizeof(float), BH_VALVEPOS_OUTCHANINDEX, TID_FLOAT); +#endif /* #ifdef BH_VALVEPOS_OUTCHANINDEX */ +#endif /* #ifdef BH_VALVEPOS_INCHANINDEX */ + + + } +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<terminates the bus driver and free's the memory allocated for the info structure + * BH_INFO.

+ *

Return: FE_SUCCESS

+ * \param info is a pointer to the DD specific info structure + */ +INT bh_exit(BH_INFO *info) +{ +#ifdef MIDEBUGF1 + cm_msg(MLOG,"",">>bh_exit()"); +#endif + + // call EXIT function of bus driver, usually closes device + info->bd(CMD_EXIT, info->bd_info); + +#ifdef SCW_USED + if (info->bh_in_settings.scw_in_use && !info->startup_error) { // slowcontrol watchdog in use + // deregister from the slowcontrol watchdog + lem_scw_exit(&info->scw); + } +#endif + + free(info); + bh_info = NULL; + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<set a value of the bronkhorst mass flow meter.

+ *

Return: FE_SUCCESS

+ * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number + * \param value to be set + */ +INT bh_set(BH_INFO *info, INT channel, float value) { + int cmd, status, decode_tag, i; + float setval; + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"",">>bh_set(channel=%d, value=%f)", channel, value); +#endif + status = BH_SUCCESS; + + // always remember that value will be stored in info->last_set[channel] + // and setval will be sent to the controller + setval = value; + + if (info->startup_error) + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + + if (!info->bd_connected) { + if (info->first_bd_error) { + info->first_bd_error = 0; + cm_msg(MINFO, "BH_set", + "BH_set: set values not possible at the moment, since the bus driver is " + "not available!"); + } + ss_sleep(10); + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + + // if dd is starting up, do not actually send a command!! + if (info->startup_tag < 2) { + + // write channel information about output and input instead + if (info->startup_tag == 0) { + float dummy; + int has_changed; + has_changed = 0; + + cm_msg(MLOG,"","Parameter .../BHFCin/DD/\"Reset to Defaults of Type\" is %c", + info->bh_in_settings.reset_to_defaults); + + if ((info->bh_in_settings.reset_to_defaults=='Y')||(info->bh_in_settings.reset_to_defaults=='y')) { + float tvalp; + INT tivalp; + HNDLE hDB; + + cm_get_experiment_database(&hDB, NULL); + + if (info->bh_in_settings.Flow_lpm_Offset != 0.0) { + tvalp = info->bh_in_settings.Flow_lpm_Offset = 0.0; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow lpmin Offset",&tvalp,sizeof(tvalp),1,TID_FLOAT); + } + if (info->bh_in_settings.FlowDAC_Offset != 0) { + tivalp = info->bh_in_settings.FlowDAC_Offset = 0; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow DAC Offset",&tivalp,sizeof(tivalp),1,TID_INT); + } + if (info->bh_in_settings.FlowDAC_Max != 32000) { + tivalp = info->bh_in_settings.FlowDAC_Max = 32000; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow DAC Maximum",&tivalp,sizeof(tivalp),1,TID_INT); + } + if (info->volume == 50.0) { + if (bh_afdiff(info->bh_in_settings.Flow_lpm_Max, 50.0) > 0.001) { + tvalp = info->bh_in_settings.Flow_lpm_Max = 50.0; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow lpmin Maximum",&tvalp,sizeof(tvalp),1,TID_FLOAT); + } + if (bh_afdiff(info->bh_in_settings.Flow_lpm_to_DAC_Scale,640.0) > 0.001) { + tvalp = info->bh_in_settings.Flow_lpm_to_DAC_Scale = 640.0; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow lpmin to DAC Scale",&tvalp,sizeof(tvalp),1,TID_FLOAT); + } + } else if (info->volume == 19.0) { + if (bh_afdiff(info->bh_in_settings.Flow_lpm_Max, 19.0) > 0.001) { + tvalp = info->bh_in_settings.Flow_lpm_Max = 19.0; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow lpmin Maximum",&tvalp,sizeof(tvalp),1,TID_FLOAT ); + } + if (bh_afdiff(info->bh_in_settings.Flow_lpm_to_DAC_Scale, 1684.210) > 0.001) { + tvalp = info->bh_in_settings.Flow_lpm_to_DAC_Scale = 1684.21; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow lpmin to DAC Scale",&tvalp,sizeof(tvalp),1,TID_FLOAT); + } + } else if (info->volume == 9.0) { + if (bh_afdiff(info->bh_in_settings.Flow_lpm_Max, 9.0) > 0.001) { + tvalp = info->bh_in_settings.Flow_lpm_Max = 9.0; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow lpmin Maximum",&tvalp,sizeof(tvalp),1,TID_FLOAT ); + } + if (bh_afdiff(info->bh_in_settings.Flow_lpm_to_DAC_Scale, 3555.560) > 0.001) { + tvalp = info->bh_in_settings.Flow_lpm_to_DAC_Scale = 3555.560; + has_changed++; + db_set_value(hDB, info->keyDDin, "Flow lpmin to DAC Scale",&tvalp,sizeof(tvalp),1,TID_FLOAT); + } + } else { + // NIY hier TODO Msg case not handled + } + if (has_changed) { + // NIY hier TODO Msg that # of DD ODB parameter were updated + cm_msg(MLOG,"","%d DD Parameter have been updated to their default values",has_changed); + } + } else { + } + + for (i=0; i < info->num_channels_out; i++) { +#ifdef BH_SETPOINT_OUTCHANINDEX + if (i == BH_SETPOINT_OUTCHANINDEX) { + // show info about channel and limits and flags + // and conversion to DAC and information about DAC + cm_msg(MLOG,"","Output Channel [%d] \"%s\" : Range to enter is | 0.0 - %.3f | " + "see BHFCin/DD/\"Flow lpmin Maximum\"", i, info->bh_out_settings.name[i], + info->bh_in_settings.Flow_lpm_Max); + cm_msg(MLOG,"","Output Channel [%d] Parameter .../DD/\"Flow [l/min] Offset\" is %.3f", i, + info->bh_in_settings.Flow_lpm_Offset); + cm_msg(MLOG,"","Output Channel [%d] Parameter .../DD/\"Flow [l/min] to DAC Scale\" is %.3f", i, + info->bh_in_settings.Flow_lpm_to_DAC_Scale); + cm_msg(MLOG,"","Output Channel [%d] Parameter .../DD/\"Flow DAC Offset\" is %d", i, + info->bh_in_settings.FlowDAC_Offset); + cm_msg(MLOG,"","Output Channel [%d] Parameter .../DD/\"Flow DAC Maximum\" is %d", i, + info->bh_in_settings.FlowDAC_Max); + // warning when DAC Maximum > 32000 + if (info->bh_in_settings.FlowDAC_Max > 32000) { + cm_msg(MERROR,"bh_set","**** WARNING Flow DAC SP. Maximum set value may not be larger than " + "32000 (100%% Flow) ****"); + } + if ( (int) ((info->bh_in_settings.Flow_lpm_Max - info->bh_in_settings.Flow_lpm_Offset) * + info->bh_in_settings.Flow_lpm_to_DAC_Scale +info->bh_in_settings.FlowDAC_Offset) > 32000) { + // warning when (maxflow-flowoffset)*scal-DACoffset > 32000 + cm_msg(MERROR,"bh_set","**** WARNING (Max Flow - offset [l/min])*scale + DAC offset" + " is larger than 32000 (100%% Flow) ****"); + } + + // valid volume ? + if (info->volume > 0.0) { + if (info->volume == 50.0) { // volume is 50 l/min He + // max flow should be less or equal 50 + if (info->bh_in_settings.Flow_lpm_Max > 50.0) { + cm_msg(MLOG,"bh_set","ERROR .../DD/\"Flow lpm Maximum\" (%.1f) is larger than 50 l/min " + "(will internally change it)", info->bh_in_settings.Flow_lpm_Max); + info->bh_in_settings.Flow_lpm_Max = 50.0; + } + + // NIY hier TODO scale should be near <= 640 + + // warning when max flow is less or equal 19 + if (info->bh_in_settings.Flow_lpm_Max <= 19.0) { + cm_msg(MLOG,"bh_set","WARNING .../DD/\"Flow lpm Maximum\" (%.1f) is rather low for " + "50 l/min", info->bh_in_settings.Flow_lpm_Max); + } + + } else if (info->volume == 19.0) { // volume is 19 l/min He + // max flow should be less or equal 19 + if (info->bh_in_settings.Flow_lpm_Max > 19.0) { + cm_msg(MLOG,"bh_set","ERROR .../DD/\"Flow lpm Maximum\" (%.1f) is larger than 19 l/min " + "(will internally change it)", info->bh_in_settings.Flow_lpm_Max); + info->bh_in_settings.Flow_lpm_Max = 19.0; + } + + // NIY hier TODO scale should be near <= 1684.21 + + // warning when max flow is less or equal 9 + if (info->bh_in_settings.Flow_lpm_Max <= 9.0) { + cm_msg(MLOG,"bh_set","WARNING .../DD/\"Flow lpm Maximum\" (%.1f) is rather low for " + "19 l/min", info->bh_in_settings.Flow_lpm_Max); + } + + } else if (info->volume == 9.0) { // volume is 9 l/min He + + // max flow should be less or equal 9 + if (info->bh_in_settings.Flow_lpm_Max > 9.0) { + cm_msg(MLOG,"bh_set","ERROR .../DD/\"Flow lpm Maximum\" (%.1f) is larger than 9 l/min" + "(will internally change it)", info->bh_in_settings.Flow_lpm_Max); + info->bh_in_settings.Flow_lpm_Max = 9.0; + } + + // NIY hier TODO scale should be near <= 3555.56 + + // } else if (info->volume == ???) { + // + } else { + cm_msg(MERROR,"bh_set","WARNING no information about this BHTModel number (%.3f l/min)! " + "Current DD Parameter might not be appropriate. " + "Add case for this volume here",info->volume); + } + + } else { + cm_msg(MERROR,"bh_set","WARNING no information about the BHTModel number available! " + "Current DD Parameter might not be appropriate"); + + } + + cm_msg(MLOG,"","Output Channel [%d] Parameter .../DD/\"Set out of Range Flow to Max\" is %c", + i, info->bh_in_settings.SetOutofRangeSetpoint_to_Max); + + cm_msg(MLOG,"","Parameter .../BHFCin/DD/\"Set out of Range FlowSP to Max\" is %c", + info->bh_in_settings.SetOutofRangeSetpoint_to_Max); + + if ((info->bh_in_settings.SetOutofRangeSetpoint_to_Max == 'Y') || + (info->bh_in_settings.SetOutofRangeSetpoint_to_Max == 'y')) { + cm_msg(MLOG,"","Output Channel [%d] *** An out of range Flow SP will be set to Maximum " + "flow (%.3f l/min)!", i, info->bh_in_settings.Flow_lpm_Max); + } else { + cm_msg(MLOG,"","Output Channel [%d] *** An out of range Flow SP will be reset to the " + "previously set Flow SP!", i); + } + continue; + } +#endif + // get command from the name + cmd = bh_get_cmd(info->bh_out_settings.name[i], &decode_tag); + switch (cmd) { + +#ifndef BH_SETPOINT_OUTCHANINDEX + // write information only when not using l/min + case BH_SETPOINT: + cm_msg(MLOG,"","Output Channel [%d] \"%s\" : Range to enter is | 0 - 32767 | " + "(32000=100%%)", i, info->bh_out_settings.name[i]); + break; +#endif + case BH_VALVEOUT: + cm_msg(MLOG,"","Output Channel [%d] \"%s\" : Range to enter is | 0 - 1 | (will be " + "scaled by %d)", i, info->bh_out_settings.name[i], BH_MAX_NEEDLE_VALVE); + break; + + default: + ; + } // switch + } // for all output channels + + for (i=0; i < info->num_channels_in; i++) { +#ifdef bronkhorst_flowmeter_inCHANINDEX //!< index of Flow readout (l/min) + if (i== bronkhorst_flowmeter_inCHANINDEX) { + cm_msg(MLOG,"","Input Channel [%d] \"%s\" : Range shown is | 0.0 - %.3f | l/min", + i, info->bh_in_settings.name[i], info->bh_in_settings.Flow_lpm_Max); + continue; + } +#endif +#ifdef BH_SETPOINT_INCHANINDEX //!< index of Flow Setpoint readout (l/min) + if (i == BH_SETPOINT_INCHANINDEX) { + cm_msg(MLOG,"","Input Channel [%d] \"%s\" : Range shown is | 0.0 - %.3f | l/min", + i, info->bh_in_settings.name[i], info->bh_in_settings.Flow_lpm_Max); + continue; + } +#endif + // get command from the name + cmd = bh_get_cmd(info->bh_in_settings.name[i], &decode_tag); + switch (cmd) { + + case BH_SETPOINT: + case BH_MEASURE: + cm_msg(MLOG,"","Input Channel [%d] \"%s\" : Range shown is | 0 - 32767 | " + "(32000=100%%)", i, info->bh_in_settings.name[i]); + break; + + case BH_VALVEOUT: + cm_msg(MLOG,"","Input Channel [%d] \"%s\" : Range shown is | 0 - 1 |" + "(reading scaled by 1/%d)", i, info->bh_in_settings.name[i], + BH_MAX_NEEDLE_VALVE); + break; + + case BH_CNTRLMODE: + cm_msg(MLOG,"","Input Channel [%d] \"%s\" : Controller modes are 0,1,3,4,5,7," + "9,12,18", i, info->bh_in_settings.name[i]); + break; + + case BH_TEMPERATURE: // 142 + cm_msg(MLOG,"","Input Channel [%d] \"%s\" : Temperature [C] range is | -250 - 500 |", + i, info->bh_in_settings.name[i]); + break; + + case BH_FLUIDTEMP: // 181 + cm_msg(MLOG,"","Input Channel [%d] \"%s\" : Fluid Temperature [C] range is | -273.15 - " + "3.40282E+38 |", i, info->bh_in_settings.name[i]); + break; + + default: + ; + } // switch + } // for all input channels + + // update internal array of input channels + for (i=0; i < info->num_channels_in; i++) { + bh_get(info, i, &dummy); + } // for all input channels + } + + info->startup_tag++; +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<>>>>>>>>>>>>>>>>>> + } + +#ifdef BH_SETPOINT_OUTCHANINDEX //!< index of Flow Setpoint (l/min) defined + if (channel == BH_SETPOINT_OUTCHANINDEX) { // channel is Flow SP (l/min) + HNDLE hDB; + INT setvali; + + cm_get_experiment_database(&hDB, NULL); + + if (value > info->bh_in_settings.Flow_lpm_Max) { // above upper limit + if ((info->bh_in_settings.SetOutofRangeSetpoint_to_Max == 'Y') || + (info->bh_in_settings.SetOutofRangeSetpoint_to_Max == 'y')) { // set to limit when Y + cm_msg(MERROR,"bh_set","Flow SP %.3f l/min is out of upper range %.3f. Will set it to the Limit", + value, info->bh_in_settings.Flow_lpm_Max); + value = info->bh_in_settings.Flow_lpm_Max; + + // update ODB channel to (new) value and continue setting + if (info->keyOut > 0) + db_set_data_index(hDB, info->keyOut, &value,sizeof(float),BH_SETPOINT_OUTCHANINDEX, TID_FLOAT); + + } else { // or reset to last value when N + cm_msg(MERROR,"bh_set","Flow SP %.3f l/min is out of upper range %.3f. Will reset it to %.3f " + "l/min",value,info->bh_in_settings.Flow_lpm_Max,info->last_set[channel]); + value = info->last_set[channel]; + + // reset ODB channel to value and return + if (info->keyOut > 0) + db_set_data_index(hDB, info->keyOut, &value,sizeof(float),BH_SETPOINT_OUTCHANINDEX,TID_FLOAT); + return FE_SUCCESS; // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + } + + } + + // NOTE Flow SP l/min < 0 is not checked here as it may still be >= 0 after conversion to FlowDAC + + // calculate DAC value + setvali = (value -info->bh_in_settings.Flow_lpm_Offset)*info->bh_in_settings.Flow_lpm_to_DAC_Scale + +info->bh_in_settings.FlowDAC_Offset; + + // check limits and if OK send command + if (setvali < 0) { // DAC value is lower than lower range + cm_msg(MERROR,"bh_set","FlowDAC SP %d is below the lower FlowDAC range 0. Will reset Flow SP " + "from %.3f l/min to %.3f l/min", setvali, value, info->last_set[channel]); + value = info->last_set[channel]; + + // reset ODB channel to value and return + if (info->keyOut > 0) + db_set_data_index(hDB, info->keyOut, &value,sizeof(float),BH_SETPOINT_OUTCHANINDEX,TID_FLOAT); + return FE_SUCCESS; // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + } + + if (setvali > info->bh_in_settings.FlowDAC_Max) { + float newval; + + // avoid division by 0 + if (info->bh_in_settings.Flow_lpm_to_DAC_Scale > 0.001) { + newval = (info->bh_in_settings.FlowDAC_Max + -info->bh_in_settings.FlowDAC_Offset)/info->bh_in_settings.Flow_lpm_to_DAC_Scale + +info->bh_in_settings.Flow_lpm_Offset; + + cm_msg(MERROR,"bh_set","FlowDAC SP %d is above the upper FlowDAC range %d. " + "Will set %d and change Flow SP from %.3f l/min to " + "%.3f l/min in the ODB", setvali, info->bh_in_settings.FlowDAC_Max, + info->bh_in_settings.FlowDAC_Max, value, newval); + setvali = info->bh_in_settings.FlowDAC_Max; + value = newval; + + // update ODB channel to value and continue setting + if (info->keyOut > 0) + db_set_data_index(hDB, info->keyOut, &value,sizeof(float),BH_SETPOINT_OUTCHANINDEX,TID_FLOAT); + } + } + setval = setvali; + } +#endif + + // get command and decode tag from the name + cmd = bh_get_cmd(info->bh_out_settings.name[channel], &decode_tag); + + // set value in the flowcontroller + status = bh_set_send_rcv(info, cmd, setval); + + if (status != BH_SUCCESS) { + if ( info->errorcount < BH_MAX_ERROR ) { + if (info->bh_in_settings.detailed_msg) + cm_msg(MERROR, "bh_set", "BH communication or command error. status = %d", status); + info->errorcount++; + } + } else { + float diff; +#ifdef BH_SETPOINT_OUTCHANINDEX //!< index of Flow Setpoint (l/min) defined + if (channel == BH_SETPOINT_OUTCHANINDEX) { // channel is Flow SP (l/min) + + diff = bh_afdiff( info->last_set[channel],value); + if (diff > 0.001) { // FlowSP l/min has changed + cm_msg(MLOG,"","Output Channel[%d] \"%s\" changed to %.3f l/min. Sending DAC Setpoint " + "%.0f (%.1f%%) to Bronkhorst.", channel, info->bh_out_settings.name[channel], + value, setval, setval/320.0); + } + } else { + + diff = bh_afdiff( info->last_setval[channel],setval); + if (diff > 0.001) { + cm_msg(MLOG,"","Output Channel[%d] \"%s\" changed to %f", channel, + info->bh_out_settings.name[channel], setval); + } + } +#else + diff = bh_afdiff( info->last_setval[channel],setval); + if (diff > 0.001) { + cm_msg(MLOG,"","Output Channel[%d] \"%s\" changed to %f", channel, + info->bh_out_settings.name[channel], setval); + } +#endif + + info->last_set[channel] = value; + info->last_setval[channel] = setval; + } + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<get a value from the bronkhorst mass flow meter.

+ *

Return: FE_SUCCESS

+ * \param info is a pointer to the DD specific info structure. + * \param channel is the channel number + * \param pvalue read + */ +INT bh_get(BH_INFO *info, INT channel, float *pvalue) +{ + int cmd, status, decode_tag; + char str[128], err_msg[128]; + DWORD nowtime, difftime; + INT tchannel; + float diff, previous_value_tchannel; + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"",">>bh_get(channel=%d)", channel); + cm_msg_flush_buffer(); +#endif + tchannel = channel; + + // error timeout facility + nowtime = ss_time(); + difftime = nowtime - info->lasterrtime; + if ( difftime > BH_DELTA_TIME_ERROR ) { + info->errorcount = 0; + info->lasterrtime = ss_time(); + } + + if (!pvalue) { + ss_sleep(10); // to keep CPU load low when Run active + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + + // check if there was a startup error + if ( info->startup_error ) { + *pvalue = (float) BH_INIT_ERROR; + ss_sleep(10); // to keep CPU load low when Run active + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + + // check if they where too many reconnection failures + if (info->reconnection_failures > BH_MAX_RECONNECTION_FAILURE) { + *pvalue = (float) BH_READ_ERROR; + if (info->reconnection_failures == BH_MAX_RECONNECTION_FAILURE+1) { + cm_msg(MERROR, "BH_get", "too many reconnection failures, bailing out :-("); + cm_msg_flush_buffer(); + info->reconnection_failures++; + } + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + +#ifdef SCW_USED + if (info->bh_in_settings.scw_in_use) { // slowcontrol watchdog in use + // feed slowcontrol watchdog + info->scw.last_updated = ss_time(); + lem_scw_update(&info->scw); + } +#endif + + // if disconnected, try to reconnect after the timeout expires + if (!info->bd_connected) { + if (ss_time()-info->last_reconnect > BH_RECONNECTION_TIMEOUT) { // timeout expired + BOOL wflag; + DWORD wtimeout,wtimeoutn; + + if (info->bh_in_settings.detailed_msg) { + cm_msg(MINFO, "BH_get", "Bronkhorst: reconnection trial ..."); + cm_msg_flush_buffer(); + } +#ifdef WATCHDOG_USED + // turn off watchdog as fe might wait forever + cm_get_watchdog_params(&wflag, &wtimeout); + wtimeoutn = 0; + cm_set_watchdog_params(wflag,wtimeoutn); +#endif + status = info->bd(CMD_INIT, info->hkey, &info->bd_info); +#ifdef WATCHDOG_USED + // restore watchdog params + cm_set_watchdog_params(wflag, wtimeout); +#endif + + if (status != FE_SUCCESS) { + info->reconnection_failures++; + *pvalue = (float) BH_READ_ERROR; + if (info->bh_in_settings.detailed_msg) { + cm_msg(MINFO, "BH_get", "Bronkhorst EL-FLOW reconnection attempt failed"); + cm_msg_flush_buffer(); + } + return FE_ERR_HW; // ----->>>>>>>>>>>>>! + } else { + info->bd_connected = 1; // bus driver is connected again + info->last_reconnect = ss_time(); + info->reconnection_failures = 0; + info->first_bd_error = 1; + + cm_msg(MINFO, "BH_get", "Bronkhorst EL-FLOW successfully reconnected"); + cm_msg_flush_buffer(); + } + } else { // wait time interval not yet expired +// cm_msg(MINFO, "BH_get", "Bronkhorst: last reconnection attempt not yet expired"); +// cm_msg_flush_buffer(); + *pvalue = info->last_valid_value[channel]; + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + } + +#ifdef bronkhorst_flowmeter_inCHANINDEX + // if FLOW liter/min channel add 1 to tchannel + if (channel == bronkhorst_flowmeter_inCHANINDEX) { //!< index of Flow readout (l/min) + tchannel += 1; + + // else if (channel is Flow liter/min channel+1) return the channels value + } else if (channel == bronkhorst_flowmeter_inCHANINDEX+1) { // Index of corresponding SP DAC channel + *pvalue = info->last_valid_value[channel]; +#ifdef MIDEBUGF1 + if (pvalue) + cm_msg(MLOG,"","<>>>>>>>>>>>>! + } +#endif + +#ifdef BH_SETPOINT_INCHANINDEX + // if FLOWSP liter/min channel add 1 to tchannel + if (channel == BH_SETPOINT_INCHANINDEX) { //!< index of Flow Setpoint readout (l/min) + tchannel += 1; + + // else if (channel is Flowliter channel+1) return the channels value + } else if (channel == bronkhorst_flowmeter_inCHANINDEX+1) { // Index of corresponding DAC channel + *pvalue = info->last_valid_value[channel]; +#ifdef MIDEBUGF1 + if (pvalue) + cm_msg(MLOG,"","<>>>>>>>>>>>>! + } +#endif + + // get command and decode tag from the name + cmd = bh_get_cmd(info->bh_in_settings.name[tchannel], &decode_tag); + if (cmd == BH_NO_CMD) { // error + *pvalue = BH_CMD_ERROR; + + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + + // get value from the flowcontroller + status = bh_get_send_rcv(info, cmd, str); + + if (status != BH_SUCCESS) { + if ( info->errorcount < BH_MAX_ERROR ) { + if (info->bh_in_settings.detailed_msg) { + cm_msg(MERROR, "BH_get", "BH communication or command error."); + cm_msg_flush_buffer(); + } + info->errorcount++; + } + info->readback_failure++; + + if (info->readback_failure == BH_MAX_READBACK_FAILURE) { + info->readback_failure = 0; + // try to disconnect and reconnect the bus driver + if ((ss_time()-info->last_reconnect > BH_RECONNECTION_TIMEOUT) && + info->bd_connected) { + + // logout ets/eds before bus driver is freed +#ifdef HAVE_ETS + if (info->bh_in_settings.ets_in_use) + ets_logout(info->bd_info, BH_ETS_LOGOUT_SLEEP, + info->bh_in_settings.detailed_msg); +#endif +#ifdef HAVE_EDS + if (info->bh_in_settings.eds_in_use) + eds_logout(info->bd_info, BH_EDS_LOGOUT_SLEEP, + info->bh_in_settings.detailed_msg); +#endif + /* info->bd(CMD_EXIT, info->bd_info); */ // disconnect bus driver + if (info->bd_info) { + free(info->bd_info); info->bd_info = NULL; + } + + cm_msg(MERROR, "BH_get", "Communication error: trying to disconnect and reconnect the " + "bus driver"); + cm_msg_flush_buffer(); + info->last_reconnect = ss_time(); + info->bd_connected = 0; + } + } + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + + // decode return hexcode string + status = bh_decode_number(str, decode_tag, err_msg, pvalue); + + if (status != BH_SUCCESS) { + if ( info->errorcount < BH_MAX_ERROR ) { + if (info->bh_in_settings.detailed_msg) + cm_msg(MERROR, "BH_get", "%s", err_msg); + info->errorcount++; + } + *pvalue = info->last_valid_value[channel]; + } else { + + // rescale needle valve position from 0..2^24 - 1 to the interval 0..1 + if (cmd == BH_VALVEOUT) { + *pvalue /= BH_MAX_NEEDLE_VALVE; + } + } + + // channel readout changed or device is not initialized yet + diff = bh_afdiff(info->last_valid_value[tchannel],*pvalue); + if ((tchannel == channel)&&((diff > 0.001)||(info->startup_tag <= 1))) { + + if (info->startup_tag <= 1) { + if (cmd == BH_CNTRLMODE) { +// +//*0 = setpoint = BUS setpoint -> also called save mode +//*1 = setpoint = analog input +// 2 = setpoint = master output (FLOW-BUS)*slave factor(FLOW-BUS) +//*3 = close valve +//*4 = controller idle (no reaction on sensor signal changes) +//*5 = testmode enable -> (select with par 70) +// 6 = tuningmode enable -> (select with par 79) +//*7 = setpoint = 100% +// 8 = purge valve (fully open) +//*9 = calibration mode enable -> (select with par 58) +// : +//*12 = setpoint = 0% +// : +// 18 = setpoint = RS232 setpoint -> bypass of save mode + + if ((int)*pvalue == 0) { + cm_msg(MLOG,"","Input Channel [%d] \"%s\" is %d : \"Setpoint = BUS setpoint\"", tchannel, + info->bh_in_settings.name[tchannel], (int)*pvalue); + } else { + cm_msg(MLOG,"","Input Channel [%d] \"%s\" is %d : \"Unexpected setting of Control mode " + "parameter!\"", tchannel, info->bh_in_settings.name[tchannel], (int)*pvalue); + cm_msg(MERROR,"bh_get","*** Check the instruction manual for \"DDE Parameter 12 Control " + "mode\" ***"); + } + } else if (cmd == BH_SETPOINT) { + cm_msg(MLOG,"","Input Channel [%d] \"%s\" initial value is %.3f", tchannel, + info->bh_in_settings.name[tchannel], *pvalue); + } else { + cm_msg(MLOG,"","Input Channel [%d] \"%s\" initial value is %.3f", tchannel, + info->bh_in_settings.name[tchannel], *pvalue); + } + } else { + if (cmd == BH_CNTRLMODE) { + cm_msg(MLOG,"","Input Channel [%d] \"%s\" changed from %d to %d", tchannel, + info->bh_in_settings.name[tchannel], (int) info->last_valid_value[tchannel], + (int)*pvalue); + } else if (cmd == BH_SETPOINT) { + cm_msg(MLOG,"","Input Channel[%d] \"%s\" changed from %.3f to %.3f", tchannel, + info->bh_in_settings.name[tchannel], + info->last_valid_value[tchannel], *pvalue); + } else { + #ifdef MIDEBUGF1 + cm_msg(MLOG,"","Input Channel[%d] \"%s\" changed from %.3f to %.3f", tchannel, + info->bh_in_settings.name[tchannel], + info->last_valid_value[tchannel], *pvalue); + #endif + } + } + + } + + previous_value_tchannel = info->last_valid_value[tchannel]; + info->last_valid_value[tchannel] = *pvalue; + info->readback_failure = 0; + + // readout was actually done for DAC channel following the Flow l/min channel + if (channel+1 == tchannel) { + + // avoid division by 0 + if (info->bh_in_settings.Flow_lpm_to_DAC_Scale > 0.001) { + float flowlpmin; + + // calculate Flow or FlowSP l/min from DAC or DAC SP + flowlpmin = (*pvalue - info->bh_in_settings.FlowDAC_Offset) + /info->bh_in_settings.Flow_lpm_to_DAC_Scale + info->bh_in_settings.Flow_lpm_Offset; + + // return Flow calculated from DAC for this channel (with 3 decimals) + *pvalue = rintf(flowlpmin*1000)/1000.0; + + // log changes or startup values + diff = bh_afdiff(info->last_valid_value[channel], *pvalue); + if ((diff > 0.001) || (info->startup_tag <= 1)) { + if (info->startup_tag <= 1) { + if (cmd == BH_SETPOINT) { + cm_msg(MLOG,"","Input Channel[%d] \"%s\" initial value is %.0f (%.1f%%)", tchannel, + info->bh_in_settings.name[tchannel], + info->last_valid_value[tchannel], + (float) info->last_valid_value[tchannel]/320.0); + cm_msg(MLOG,"","Input Channel[%d] \"%s\" initial value is %.3f l/min", channel, + info->bh_in_settings.name[channel], *pvalue); + } else { + #ifdef MIDEBUGF1 + cm_msg(MLOG,"","Input Channel[%d] \"%s\" initial value is %.0f (%.1f%%)", tchannel, + info->bh_in_settings.name[tchannel], + info->last_valid_value[tchannel], + (float) info->last_valid_value[tchannel]/320.0); + cm_msg(MLOG,"","Input Channel[%d] \"%s\" initial value is %.3f l/min", channel, + info->bh_in_settings.name[channel], *pvalue); + #endif + } + } else { + if (cmd == BH_SETPOINT) { + cm_msg(MLOG,"","Input Channel[%d] \"%s\" changed from %.0f (%.1f%%) to %.0f (%.1f%%)", + tchannel, info->bh_in_settings.name[tchannel], previous_value_tchannel, + (float)previous_value_tchannel/320.0, info->last_valid_value[tchannel], + (float) info->last_valid_value[tchannel]/320.0); + cm_msg(MLOG,"","Input Channel[%d] \"%s\" changed from %.3f to %.3f l/min", channel, + info->bh_in_settings.name[channel], info->last_valid_value[channel], *pvalue); + } else { + #ifdef MIDEBUGF1 + cm_msg(MLOG,"","Input Channel[%d] \"%s\" changed from %.0f (%.1f%%) to %.0f (%.1f%%)", + tchannel, info->bh_in_settings.name[tchannel], previous_value_tchannel, + (float)previous_value_tchannel/320.0, info->last_valid_value[tchannel], + (float) info->last_valid_value[tchannel]/320.0); + cm_msg(MLOG,"","Input Channel[%d] \"%s\" changed from %.3f to %.3f l/min", channel, + info->bh_in_settings.name[channel],info->last_valid_value[channel],*pvalue); + #endif + } + } + } + + info->last_valid_value[channel] = *pvalue; + } + } + if (channel == BH_IN_CHANNELS -1) + ss_sleep(100); + +#ifdef MIDEBUGF1 + if (pvalue) + cm_msg(MLOG,"","<>bh_get_show_param(cmd=%d, decode_tag=%d, para_name=%s)", cmd, + decode_tag, para_name); +#endif + // check if there was a startup error + if ( info->startup_error ) { + ss_sleep(10); // to keep CPU load low when Run active + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } + + // get value from the flowcontroller + status = bh_get_send_rcv(info, cmd, reply); + if (status == BH_SUCCESS) { + // string was returned + if (decode_tag == BH_STRING) { + char result[128]; + + status = bh_decode_string(reply, error_msg, result); + if (status == BH_SUCCESS) { + cm_msg(MLOG,"","DDE Parameter %d : %s = %s", cmd, para_name, result); + } else { + cm_msg(MERROR,"bh_get_show_param","cmd=%d, para_name=%s: bh_decode_string() " + "returned : %s", cmd, para_name, error_msg); + } + + // char, integer or float + // BH_CHAR, BH_INTEGER, BH_FLOAT, BH_LONG + } else { + float result; + + result = -1.f; + status = bh_decode_number(reply, decode_tag, error_msg, &result); + if (status == BH_SUCCESS) { + if ((decode_tag == BH_FLOAT) || (decode_tag == BH_LONG)) { + cm_msg(MLOG,"","DDE Parameter %d : %s = %f", cmd, para_name, result); + } else if (decode_tag == BH_CHAR) { + cm_msg(MLOG,"","DDE Parameter %d : %s = %d = 0x0%x", cmd, para_name, (int)result, (unsigned int)result); + } else { + cm_msg(MLOG,"","DDE Parameter %d : %s = %d", cmd, para_name, (int)result); + } + } else { + cm_msg(MERROR,"bh_get_show_param","cmd=%d, para_name=%s: bh_decode_number() " + "returned : %s", cmd, para_name, error_msg); + cm_msg(MLOG,"","DDE Parameter %d : %s = ???", cmd, para_name); + } + } + } else { + cm_msg(MLOG,"","DDE Parameter %d : %s = ???", cmd, para_name); + } + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<>sh_show_param()"); +#endif + + bh_get_show_param(info, BH_SENSORTYPE, BH_CHAR, "Sensor type"); // 22 + bh_get_show_param(info, BH_FLUIDNAME, BH_STRING, "Fluid name"); // 25 10char + bh_get_show_param(info, BH_VALVEOFFSET,BH_INTEGER,"Valve offset"); // 59 + bh_get_show_param(info, BH_IOSTATUS, BH_CHAR, "IO status"); // 86 + bh_get_show_param(info, BH_DEVICETYPE, BH_STRING, "Device type"); // 90 6 char + bh_get_show_param(info, BH_MODELNUM, BH_STRING, "BHTModel number"); // 91 10 char? + bh_get_show_param(info, BH_SERIALNUM, BH_STRING, "Serial number"); // 92 10 char? + bh_get_show_param(info, BH_VERSION, BH_STRING, "Firmware version");// 105 6 char + bh_get_show_param(info, BH_TEMPERATURE,BH_FLOAT, "Temperature"); // 142 +//bh_get_show_param(info, BH_CALIBVOLLT, BH_FLOAT, "Calibrated Volume"); // 145 not relevant? +//bh_get_show_param(info, BH_PRESSURE, BH_FLOAT, "Pressure"); // 143 not implemented? + bh_get_show_param(info, BH_CONTRFEAT, BH_CHAR, "Controller features"); + bh_get_show_param(info, BH_PID_KP, BH_FLOAT, "PID factor Kp"); + bh_get_show_param(info, BH_PID_TI, BH_FLOAT, "PID factor Ti"); + bh_get_show_param(info, BH_PID_TD, BH_FLOAT, "PID factor Td"); +//bh_get_show_param(info, BH_FLUIDTEMP, BH_FLOAT, "Fluid Temperature"); // 181 not relevant? + +#ifdef MIDEBUGF1 + cm_msg(MLOG,"","<at startup, after initialization of the DD, this routine allows to write + * default names of the in-channels into the ODB.

+ *

Return: FE_SUCCESS + * \param info is a pointer to the DD specific info structure + * \param channel of the name to be set + * \param name pointer to the ODB name + */ +INT bh_in_get_label(BH_INFO *info, INT channel, char *name) { + if (name) { + *name = '\0'; + if ((channel >= 0) && (channel < BH_IN_CHANNELS)) { + strncpy(name, info->bh_in_settings.name[channel], NAME_LENGTH-1); + *(name+NAME_LENGTH-1) = '\0'; + } else { + cm_msg(MERROR,"bh_in_get_label","Channel number out of range 0 - %d",BH_IN_CHANNELS-1); + } + } else { + cm_msg(MERROR,"bh_in_get_label","NULL pointer to return label"); + } + cm_msg_flush_buffer(); + return FE_SUCCESS; +} + + +/*----------------------------------------------------------------------------*/ +/*! + *

at startup, after initialization of the DD, this routine allows to write + * default thresholds of the channels into the ODB.

+ *

Return: FE_SUCCESS + * \param info is a pointer to the DD specific info structure + * \param channel of the threshold to be set + * \param pvalue pointer to the returned threshold + */ +INT bh_in_get_default_threshold(BH_INFO * info, INT channel, float *pvalue) +{ + if (pvalue == NULL) { +#ifdef MIDEBUGF2 + cm_msg(MLOG, "", ">>bh_in_get_default_threshold(%d,NULL)", channel); + cm_msg_flush_buffer(); +#endif + return FE_SUCCESS; // ----->>>>>>>>>>>>>! + } +#ifdef MIDEBUGF2 + cm_msg(MLOG, "", ">>bh_in_get_default_threshold(%d)", channel); +#endif + if ((channel >= 0) && (channel < info->num_channels_in)) { + switch (channel) { + case 0: // Flow (l/min) + case 2: // Flow_SP (readback l/min) + *pvalue = 0.01f; + break; + case 1: // FlowDAC + case 3: // FlowDAC_SP (readback) + case 4: // ControlMode (readback) + *pvalue = 0.1f; + break; + case 5: // Temperature (C) + *pvalue = 0.001f; + break; + case 6: // ValvePosition(readback 0..1) + *pvalue = 0.0001f; + break; + + default: + *pvalue = 0.1f; + } + } else { +#ifdef MIDEBUGF2 + cm_msg(MLOG,"","<num_channels_in-1); +#endif + } + +#ifdef MIDEBUGF2 + cm_msg(MLOG, "", "bh_in_get_default_threshold(%d,%f)", channel, *pvalue); + cm_msg_flush_buffer(); +#endif + return FE_SUCCESS; +} + +/*----------------------------------------------------------------------------------*/ +/*! + *

at startup, after initialization of the DD, this routine allows to write + * default names of the out-channels into the ODB.

+ *

Return: FE_SUCCESS + * \param info is a pointer to the DD specific info structure + * \param channel of the name to be set + * \param name pointer to the ODB name + */ +INT bh_out_get_label(BH_INFO *info, INT channel, char *name) { + if (name) { + if ((channel >= 0) && (channel < BH_OUT_CHANNELS)) { + strncpy(name, info->bh_out_settings.name[channel],NAME_LENGTH-1); + name[NAME_LENGTH-1] = '\0'; + } else { + cm_msg(MERROR,"bh_out_get_label","Channel number out of range | 0 - %d |",BH_OUT_CHANNELS-1); + } + } else { + cm_msg(MERROR,"bh_out_get_label","NULL pointer to return label"); + } + cm_msg_flush_buffer(); + return FE_SUCCESS; +} + +/*---- device driver entry point ---------------------------------------------------*/ +INT bronkhorst_flowmeter_in(INT cmd, ...) +{ + va_list argptr; + HNDLE hKey; + INT channel, status; + float *pvalue; + BH_INFO *info; + BH_INFO **pinfo; + char *name; + + va_start(argptr, cmd); + status = FE_SUCCESS; + +#ifdef MIDEBUGF3 + cm_msg(MLOG,"",">>bronkhorst_flowmeter_in(cmd=%d)",cmd); +#endif + + switch (cmd) { + case CMD_INIT: // 1 + { + hKey = va_arg(argptr, HNDLE); + pinfo = va_arg(argptr, BH_INFO **); + channel = va_arg(argptr, INT); + va_arg(argptr, DWORD); // flags - currently not used + func_t *bd = va_arg(argptr, func_t *); + status = bh_in_init(hKey, pinfo, channel, bd); + } + break; + + case CMD_EXIT: // 2 + info = va_arg(argptr, BH_INFO *); + status = bh_exit(info); + break; + + case CMD_GET: // 20 + info = va_arg(argptr, BH_INFO *); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float*); + status = bh_get(info, channel, pvalue); + break; + + case CMD_GET_LABEL: // 10 + info = va_arg(argptr, BH_INFO *); + channel = va_arg(argptr, INT); + name = va_arg(argptr, char *); + status = bh_in_get_label(info, channel, name); + break; + + case CMD_GET_THRESHOLD: // 6 + info = va_arg(argptr, BH_INFO *); + channel = va_arg(argptr, INT); + pvalue = va_arg(argptr, float *); + status = bh_in_get_default_threshold(info, channel, pvalue); + break; + + // case SET_LABEL: // 9 is not handled + + default: + break; + } + + va_end(argptr); + return status; +} + +/************************************************************************/ + +INT bronkhorst_flowmeter_out(INT cmd, ...) +{ + va_list argptr; + HNDLE hKey; + INT channel, status; + float value; + BH_INFO *info; + char *name; + + va_start(argptr, cmd); + status = FE_SUCCESS; + +#ifdef MIDEBUGF3 + cm_msg(MLOG,"",">>bh_out(cmd=%d)",cmd); +#endif + + switch (cmd) { + case CMD_INIT: // 1 + { + hKey = va_arg(argptr, HNDLE); + BH_INFO **pinfo = va_arg(argptr, BH_INFO**); + channel = va_arg(argptr, INT); + va_arg(argptr, DWORD); // flags - currently not used + func_t *bd = va_arg(argptr, func_t *); + status = bh_out_init(hKey, pinfo, channel, bd); + } + break; + + case CMD_SET: // 13 + info = va_arg(argptr, BH_INFO*); + channel = va_arg(argptr, INT); + value = (float) va_arg(argptr, double); + status = bh_set(info, channel, value); + break; + +// NO 9 SET_LABEL + + case CMD_GET_LABEL: // 10 + info = va_arg(argptr, BH_INFO*); + channel = va_arg(argptr, INT); + name = va_arg(argptr, char *); + status = bh_out_get_label(info, channel, name); + break; + + default: + break; + } + + va_end(argptr); + + return status; +} + +/*------------------------------------------------------------------*/ diff --git a/device/bronkhorst_flowmeter.h b/device/bronkhorst_flowmeter.h new file mode 100644 index 0000000..9591247 --- /dev/null +++ b/device/bronkhorst_flowmeter.h @@ -0,0 +1,21 @@ +/********************************************************************\ + + Name: bronkhorst.h + Created by: Andreas Suter 2003/09/12 + + Contents: bronkhorst He flowmeter driver + +\********************************************************************/ + +/*---- device driver declaration -----------------------------------*/ +/*! + *

bronkhorst device driver (DD) for reading. It is handling the communication + * between the bronkhorst mass flow meter and midas. + */ +INT bronkhorst_flowmeter_in(INT cmd, ...); + +/*! + *

bronkhorst device driver (DD) for setting values. It is handling the + * communication between the bronkhorst mass flow meter and midas. + */ +INT bronkhorst_flowmeter_out(INT cmd, ...); diff --git a/frontend/bronkhorst_flowmeter_fe.cxx b/frontend/bronkhorst_flowmeter_fe.cxx new file mode 100644 index 0000000..d002860 --- /dev/null +++ b/frontend/bronkhorst_flowmeter_fe.cxx @@ -0,0 +1,164 @@ +/********************************************************************\ + + Name: BRONKHGFC_gfclowc0.c (modified bronkhorst_test.c of SA35) + Created by: Andreas Suter + + Contents: Midas frontend to readout/set the Bronkhorst gas flow + controller. + + $Log: BRONKHGFC_gfcflowc0.c,v $ + Revision 1.3 2024/06/30 09:38:01 raselli + New version setting flow [l/min] instead of scaled DAC + + Revision 1.2 2023/07/11 14:17:18 raselli + Increased number of Input channels + + Revision 1.1 2022/11/15 13:34:09 raselli + Adapted version of Bronkhorst flow controller originally from LEM (A. Suter) + + Revision 1.3 2004/05/11 15:54:22 nemu + adopted to new CVS structure + + Revision 1.2 2003/10/01 08:53:23 nemu + adopted to the changes in the bronkhorst device driver + + Revision 1.1 2003/09/15 10:16:26 nemu + add bronkhorst test slowcontrol frontend files + + +\********************************************************************/ + +#include +#include "midas.h" +#include "class/multi.h" +#include "bronkhorst_flowmeter.h" +#include "bus/tcpip.h" + +/*-- Globals -------------------------------------------------------*/ + +/* The frontend name (client name) as seen by other MIDAS clients */ +char *frontend_name = "BRONKHGFC gfcflowc0"; + +/* The frontend file name, don't change it */ +char *frontend_file_name = __FILE__; + +/* frontend_loop is called periodically if this variable is TRUE */ +BOOL frontend_call_loop = FALSE; + +/* 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; + +extern BOOL should_exit; + +/*! + * 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 list ------------------------------------------------*/ + +/* device driver list */ + +DEVICE_DRIVER bh_driver[] = { + { "BHFCin", bronkhorst_flowmeter_in, 7, tcpip, DF_INPUT }, + { "BHFCout", bronkhorst_flowmeter_out, 2, tcpip, DF_OUTPUT }, + { "" } +}; + +EQUIPMENT equipment[] = { + + { "gfcflowc0", /* equipment name */ + {212, 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_multi_read, /* readout routine */ + cd_multi, /* class driver main routine */ + bh_driver, /* device driver list */ + NULL, /* init string */ + }, + + { "" } +}; + + + +/*-- Dummy routines ------------------------------------------------*/ + +INT poll_event(INT source, INT count, BOOL test) {return 1;}; +INT interrupt_configure(INT cmd, INT source, PTYPE adr) {return 1;}; + +/*-- Frontend Init -------------------------------------------------*/ + +INT frontend_init() +{ + return CM_SUCCESS; +} + +/*-- Frontend Exit -------------------------------------------------*/ + +INT frontend_exit() +{ + return CM_SUCCESS; +} + +/*-- Frontend Loop -------------------------------------------------*/ + +INT frontend_loop() +{ + return CM_SUCCESS; +} + +/*-- Begin of Run --------------------------------------------------*/ + +INT begin_of_run(INT run_number, char *error) +{ + return CM_SUCCESS; +} + +/*-- End of Run ----------------------------------------------------*/ + +INT end_of_run(INT run_number, char *error) +{ + return CM_SUCCESS; +} + +/*-- Pause Run -----------------------------------------------------*/ + +INT pause_run(INT run_number, char *error) +{ + return CM_SUCCESS; +} + +/*-- Resuem Run ----------------------------------------------------*/ + +INT resume_run(INT run_number, char *error) +{ + return CM_SUCCESS; +} + +/*------------------------------------------------------------------*/