From 2966a33233761dc2dff10e7bc0c09b8af99b3388 Mon Sep 17 00:00:00 2001
From: smathis 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/ Remove trailing blanks (and carriage control characters) from char buf decodes for any error messages coming from the bronkhorst flow meter. return error tag:
+ * - BH_SUCCESS = no error
+ * - BH_STATUS_ERR = bronkhost status error
+ * Return:
+ * - BH_SUCCESS if everthing went fine
+ * - BH_DECODE_ERR otherwise
+ * 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
+ * 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, "", "< 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,"","< Return: decode command tag
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:
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,"","<
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,"","<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,"","<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,"","<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,"","< 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