Files
mathis_s 2966a33233
Test And Build / Format (push) Failing after 3s
Test And Build / Lint (push) Failing after 14s
Initial compiling version of the bronkhorst driver
2026-07-24 15:24:08 +02:00

2792 lines
97 KiB
C++

/********************************************************************\
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/<Abbreviated Name of Device>in/DD")
for Input and .../<Abbreviated Name of Device>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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#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
/*!
* <p> generally it stores internal informations within the DD. In this
* case this are the names for the ODB describing the <b>in-channels</b>.
*/
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;
/*!
* <p> generally it stores internal informations within the DD. In this
* case this are the names for the ODB describing the <b>out-channels</b>.
*/
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\
";
/*!
* <p> This structure contains private variables for the device driver.
*/
typedef struct {
BH_IN_SETTINGS bh_in_settings; //!< internal DD settings for the <b>in-channels</b>
BH_OUT_SETTINGS bh_out_settings; //!< internal DD settings for the <b>out-channels</b>
#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 <b>in-channels</b>
INT num_channels_out; //!< number of <b>out-channels</b>
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/<name>/Variables/Output
HNDLE keyDDin; //!< HNDLE to BHFCin/DD
char name[NAME_LENGTH]; //!< equipment <name>
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;
}
/*----------------------------------------------------------------------------*/
/*!
* <p> Remove trailing blanks (and carriage control characters) from char buf </p>
*/
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;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>decodes for any error messages coming from the bronkhorst flow meter.</p>
* <p><b>return error tag:</b>
* - BH_SUCCESS = no error
* - BH_STATUS_ERR = bronkhost status error
* </p>
* \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<error code>"
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<error><error_byte>
// : 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,"","<<bh_success(ERROR %s)", error_msg);
#endif
return BH_STATUS_ERR;
} else {
return BH_SUCCESS;
}
}
/*----------------------------------------------------------------------------------*/
/*!
* converts a hex string into a readable ascii string.
*
* \param str hex input string
* \param len length of 'str'
* \param result converted ascii string
*/
void hex2ascii(char *str, int len, char *result)
{
int i, j;
char hexchar[3];
strcpy(result, "");
for (i=0; i<len; i++) {
strncpy(hexchar, str+2*i, 2);
hexchar[2]='\0';
sscanf(hexchar, "%x", &j);
strcat(result, (const char *)&j);
}
}
/*----------------------------------------------------------------------------------*/
/*!
* <p> if the return message of the flowcontroller is a number, this routine
* is used to decode the return string into a number.</p>
* <p><b>Return:</b>
* - BH_SUCCESS if everthing went fine
* - BH_DECODE_ERR otherwise
* </p>
* \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;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>if the return message of the flowcontroller is a string, this routine
* is used to decode the return string.</p>
* <p><b>Return:</b>
* - BH_SUCCESS if everthing went fine
* - BH_DECODE_ERR otherwise
* </p>
* \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;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>routine which is used to read back values from the flowcontroller.</p>
* <p><b>Return:</b>
* - 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, "", "<<bh_get_send_rcv() Reply of query %s is %s", send, str);
#endif
}
return BH_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>routine which is used to set values of the flowcontroller.</p>
* <p><b>Return:</b>
* - 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,"","<<bh_set_send_rcv(cmd=%d) BH_COMM_ERR", cmd);
#endif
return BH_COMM_ERR; // ----->>>>>>>>>>>>>!
}
// 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,"","<<bh_set_send_rcv(cmd=%d) BH_COMM_ERR", cmd);
#endif
return BH_COMM_ERR; // ----->>>>>>>>>>>>>!
}
#ifdef MIDEBUGF1
cm_msg(MLOG,"","<<bh_set_send_rcv(cmd=%d) BH_SUCCESS", cmd);
#endif
return BH_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>routine which filters the channel name and generates the decode command tag</p>
* <p><b>Return:</b> decode command tag</p>
* \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,"","<<bh_get_cmd(%s) = %d", str, cmd);
#endif
return cmd;
}
/*---- device driver routines ---------------------------------------------------------*/
/*!
* <p>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.</p>
* <p><b>Return:</b>
* - FE_SUCCESS if everything went smooth
* - FE_ERR_ODB otherwise
* \param hKey is the device driver handle given from the class driver
* \param pinfo is needed to store the internal info structure
* \param channels is the number of channels of the device (from the class driver)
* \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: <Bell>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,"","<<bh_in_init()");
#endif
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>Initializes the bronkhorst device driver, i.e. generates all the necessary
* structures in the ODB if necessary, <b>no</b> initialization of the bus driver etc.
* is done, since this is part of BH_in_init.</p>
* <p><b>Return:</b>
* - FE_SUCCESS if everything went smooth
* - FE_ERR_ODB otherwise
* \param hKey is the device driver handle given from the class driver
* \param pinfo is needed to store the internal info structure
* \param channels is the number of channels of the device (from the class driver)
* \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,"","<<bh_out_init()");
#endif
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>terminates the bus driver and free's the memory allocated for the info structure
* BH_INFO.</p>
* <p><b>Return:</b> FE_SUCCESS</p>
* \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,"","<<bh_exit()");
#endif
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>set a value of the bronkhorst mass flow meter.</p>
* <p><b>Return:</b> FE_SUCCESS</p>
* \param info is a pointer to the DD specific info structure.
* \param channel is the channel number
* \param value 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,"","<<bh_set(channel=%d) startup_tag < 2 - No command sent to controller", channel);
#endif
return FE_SUCCESS; // --->>>>>>>>>>>>>>>>>>>
}
#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,"","<<bh_set(channel=%d, value=%f, setval=%f)", channel, value, setval);
#endif
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>get a value from the bronkhorst mass flow meter.</p>
* <p><b>Return:</b> FE_SUCCESS</p>
* \param info is a pointer to the DD specific info structure.
* \param channel is the channel number
* \param pvalue read
*/
INT 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,"","<<bh_get(channel=%d, *pvalue=%f)", channel, *pvalue);
#endif
return FE_SUCCESS; // ----->>>>>>>>>>>>>!
}
#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,"","<<bh_get(channel=%d, *pvalue=%f)", channel, *pvalue);
#endif
return FE_SUCCESS; // ----->>>>>>>>>>>>>!
}
#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(channel=%d, *pvalue=%f)", channel, *pvalue);
#endif
cm_msg_flush_buffer();
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
INT bh_get_show_param(BH_INFO *info, INT cmd, INT decode_tag, char *para_name) {
int status;
char reply[128], error_msg[128];
#ifdef MIDEBUGF1
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,"","<<bh_get_show_param()");
#endif
cm_msg_flush_buffer();
return BH_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
INT bh_show_param(BH_INFO *info) {
#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,"","<<bh_show_param()");
#endif
return BH_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>at startup, after initialization of the DD, this routine allows to write
* default names of the in-channels into the ODB.</p>
* <p><b>Return:</b> 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;
}
/*----------------------------------------------------------------------------*/
/*!
* <p>at startup, after initialization of the DD, this routine allows to write
* default thresholds of the channels into the ODB.</p>
* <p><b>Return:</b> 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,"","<<bh_in_get_default_threshold(%d) Channel out of range |0 - %d|",
channel, info->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;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>at startup, after initialization of the DD, this routine allows to write
* default names of the out-channels into the ODB.</p>
* <p><b>Return:</b> 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;
}
/*------------------------------------------------------------------*/