Files
uwave/device/uWave.cxx
T

691 lines
22 KiB
C++

/********************************************************************\
Name: uWave.cxx
Created by: Zaher Salman 05/03/2021
Contents: Device driver for the RPi uWave controller.
TCP/IP calls sent to the RPi.
TCPIP: IEEE-448
termination: \n "new line"
\********************************************************************/
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <cstring>
#include <cmath>
#include "midas.h"
#include "mfe.h"
#include "msystem.h"
#include "uWave.h"
//----- globals -----------------------------------------------------
#define UWAVE_MAX_ERROR 10 //!< maximum number of error messages
//! timeout in (ms) for the communication between pc and uWave
#define UWAVE_TIME_OUT 20000
#define UWAVE_TIME_OUT_SLOW 100000
//! timeout in (sec) between logout terminal server an reconnection trial
#define UWAVE_RECONNECTION_TIMEOUT 5
//! maximum number of readback failures before a reconnect will take place
#define UWAVE_MAX_READBACK_FAILURE 5
//! maximum number of reconnections before bailing off
#define UWAVE_MAX_RECONNECTION_FAILURE 5
//--------- to handle error messages -------------------------------
#define UWAVE_SUCCESS 1
#define UWAVE_STANDBY 0 //!< uWave standby tag
#define UWAVE_CMD_ERROR -3 //!< invalid command error
#define UWAVE_INIT_ERROR -2 //!< initialize error tag
#define UWAVE_READ_ERROR -1 //!< read error tag
#define UWAVE_DELTA_TIME_ERROR 3600 //!< reset error counter after _DELTA_TIME_ERROR seconds
//---------- channel definition tags --------------------------------
#define UWAVE_IN_PH1_RB 0
#define UWAVE_IN_PH2_RB 1
#define UWAVE_IN_AMPL_RB 2
#define UWAVE_IN_FREQ_RB 3
#define UWAVE_IN_ONOFF_RB 4
#define UWAVE_OUT_PH1 0
#define UWAVE_OUT_PH2 1
#define UWAVE_OUT_AMPL 2
#define UWAVE_OUT_FREQ 3
#define UWAVE_OUT_ONOFF 4
//-------------------------------------------------------------------
/*!
* <p> Generally it stores internal informations within the DD. In this
* case these are the names for the ODB describing the <b>in-channels</b>.
*/
typedef struct {
INT detailed_msg; //!< flag 0: no add. msg, 1: some add. msg, 2: debug
char name[5][NAME_LENGTH]; //!< input channel names
} UWAVE_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[5][NAME_LENGTH]; //!< output channel names
} UWAVE_OUT_SETTINGS;
//! Initializing string for the UWAVE_IN_SETTINGS structure.
const char *uWave_in_settings_str =
"Detailed Messages = FLOAT : 0\n\
Input = STRING[5]:\n\
[32] Phase 1 (deg)\n\
[32] Phase 2 (deg)\n\
[32] Amplitude (dBm)\n\
[32] Frequency (GHz)\n\
[32] On/Off (1/0)\n\
";
//! initializing string for the UWAVE_OUT_SETTINGS structure.
const char *uWave_out_settings_str =
"Output = STRING[5] :\n\
[32] Phase 1 (deg)\n\
[32] Phase 2 (deg)\n\
[32] Amplitude (dBm)\n\
[32] Frequency (GHz)\n\
[32] On/Off (1/0)\n\
";
/*!
* <p> This structure contains private variables for the device driver.
*/
typedef struct {
UWAVE_IN_SETTINGS uWave_in_settings; //!< internal DD settings for the <b>in-channels</b>
UWAVE_OUT_SETTINGS uWave_out_settings; //!< internal DD settings for the <b>out-channels</b>
HNDLE hkey; //!< ODB key for bus driver info
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
DWORD last_reconnect; //!< timer for bus driver reconnect error handling
INT reconnection_failures; //!< how often reconnection failed
INT ph1; //!< Phase1 in degrees
INT ph2; //!< Phase2 in degrees
INT ampl; //!< Amplitude in dBm
INT freq; //!< Frequency in GHz
INT onoff; //!< On/Off state (1/0)
INT ph1_sp; //!< keep Phase1 in degrees
INT ph2_sp; //!< keep Phase2 in degrees
INT ampl_sp; //!< keep Amplitude in dBm
INT freq_sp; //!< keep Frequency in GHz
INT onoff_sp; //!< keep On/Off state (1/0)
} UWAVE_INFO;
UWAVE_INFO *uWave_info; //!< global info structure since port can only be initialized once.
typedef INT(func_t) (INT cmd, ...);
/*---- device driver routines ---------------------------------------------------------*/
/*!
* <p>Initializes the uWave device driver, i.e. generates all the necessary
* structures in the ODB if necessary, initializes the bus driver and the uWave.</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 uWave_in_init(HNDLE hKey, UWAVE_INFO **pinfo, INT channels, func_t *bd)
{
INT status, size;
HNDLE hDB, hKeyLocal;
char cmd[1024], rcv[128];
float fval;
cm_get_experiment_database(&hDB, NULL);
// allocate info structure
UWAVE_INFO *info = (UWAVE_INFO*)calloc(1, sizeof(UWAVE_INFO));
uWave_info = info;
*pinfo = info;
// create uWave settings record
status = db_create_record(hDB, hKey, "DD/uWave", uWave_in_settings_str);
if (status != DB_SUCCESS)
return FE_ERR_ODB;
// copy DD entries into the info structure
db_find_key(hDB, hKey, "DD/uWave", &hKeyLocal);
db_open_record(hDB, hKeyLocal, &info->uWave_in_settings, sizeof(info->uWave_in_settings), MODE_READ, NULL, NULL);
// initialize driver
info->hkey = hKey;
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;
if (!bd)
return FE_ERR_ODB;
// initialize bus driver
status = info->bd(CMD_INIT, info->hkey, &info->bd_info);
if (status != SUCCESS) {
info->startup_error = 1;
info->bd_connected = FALSE;
cm_msg(MINFO, "uWave_in_init", "Bus driver failed to initialize! status=%d",status);
return status;
}
cm_msg(MINFO, "uWave_in_init", "Bus driver initialized!");
info->bd_connected = TRUE;
// read back from ODB
status = db_find_key(hDB, 0, "/Equipment/uWave/Variables/Output", &hKeyLocal);
if (status != DB_SUCCESS) {
cm_msg(MERROR, "uWave_in_init", "uWave_in_init: couldn't obtain readback ...");
cm_yield(0);
info->startup_error = UWAVE_INIT_ERROR;
return FE_SUCCESS;
}
// read back from ODB Phase 1
size = sizeof(fval);
db_get_data_index(hDB, hKeyLocal, &fval, &size, UWAVE_OUT_PH1, TID_FLOAT);
info->ph1 = fval;
// read back from ODB Phase 2
size = sizeof(fval);
db_get_data_index(hDB, hKeyLocal, &fval, &size, UWAVE_OUT_PH2, TID_FLOAT);
info->ph2 = fval;
// read back from ODB Amplitude
size = sizeof(fval);
db_get_data_index(hDB, hKeyLocal, &fval, &size, UWAVE_OUT_AMPL, TID_FLOAT);
info->ampl = fval;
// read back from ODB Frequency
size = sizeof(fval);
db_get_data_index(hDB, hKeyLocal, &fval, &size, UWAVE_OUT_FREQ, TID_FLOAT);
info->freq = fval;
if (info->uWave_in_settings.detailed_msg == 2) {
cm_msg(MINFO, "uWave_in_init", "uWave_in_init: phase 1 = %f", info->ph1);
cm_yield(0);
}
// read back from ODB On/Off
size = sizeof(fval);
db_get_data_index(hDB, hKeyLocal, &fval, &size, UWAVE_OUT_ONOFF, TID_FLOAT);
info->onoff = (int)fval;
// How to check that the device is responding
strcpy(cmd, "OUTP?\n");
BD_PUTS(cmd);
status = BD_GETS(rcv, sizeof(rcv), "\n", UWAVE_TIME_OUT);
if (status) {
sscanf(rcv, "%f", &fval);
cm_msg(MINFO, "uWave_in_init", "uWave initialized ...");
cm_yield(0);
} else {
info->startup_error = UWAVE_INIT_ERROR;
cm_msg(MINFO, "uWave_in_init", "uWave driver failed to initialize ...");
cm_yield(0);
}
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>Initializes the uWave 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 uWave_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 uWave_out_init(HNDLE hKey, UWAVE_INFO **pinfo, INT channels, func_t *bd)
{
INT status;
HNDLE hDB, hKeyLocal;
cm_get_experiment_database(&hDB, NULL);
UWAVE_INFO *info = uWave_info;
*pinfo = info;
/* create uWave settings record */
status = db_create_record(hDB, hKey, "DD", uWave_out_settings_str);
if (status != DB_SUCCESS)
return FE_ERR_ODB;
// copy DD entries into the info structure
db_find_key(hDB, hKey, "DD", &hKeyLocal);
db_open_record(hDB, hKeyLocal, &info->uWave_out_settings,
sizeof(info->uWave_out_settings), MODE_READ, NULL, NULL);
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>terminates the bus driver and free's the memory allocated for the info structure
* UWAVE_INFO.</p>
* <p><b>Return:</b> FE_SUCCESS</p>
* \param info is a pointer to the DD specific info structure
*/
INT uWave_exit(UWAVE_INFO *info)
{
if (info->startup_error)
return FE_SUCCESS;
// call EXIT function of bus driver, usually closes device
info->bd(CMD_EXIT, info->bd_info);
free(info);
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>set a value of the uWave.</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 uWave_set(UWAVE_INFO *info, INT channel, float value)
{
char cmd[128];
// Debug message
// cm_msg(MINFO, "uWave_set", "uWave_set: %d, set cmd: %f", channel, 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, "uWave_set",
"uWave_set: set values not possible at the moment, since the bus driver is not available!");
}
ss_sleep(10);
return FE_SUCCESS;
}
// set command should go here
switch (channel) {
// Phase 1
case UWAVE_OUT_PH1: // set Phase 1 in degrees
sprintf(cmd, "PH1 %f\n",value);
if (info->uWave_in_settings.detailed_msg == 2) // debug only
cm_msg(MINFO, "uWave_set", "uWave_set: cmd: %s", cmd);
BD_PUTS(cmd);
info->ph1_sp = value;
return FE_SUCCESS;
break;
// Phase 2
case UWAVE_OUT_PH2: // set Phase 2 in degrees
sprintf(cmd, "PH2 %f\n",value);
if (info->uWave_in_settings.detailed_msg == 2) // debug only
cm_msg(MINFO, "uWave_set", "uWave_set: cmd: %s", cmd);
BD_PUTS(cmd);
info->ph2_sp = value;
return FE_SUCCESS;
break;
// Amplitude
case UWAVE_OUT_AMPL: // set Amplitude in dBm
sprintf(cmd, "AMPL %f\n",value);
if (info->uWave_in_settings.detailed_msg == 2) // debug only
cm_msg(MINFO, "uWave_set", "uWave_set: cmd: %s", cmd);
BD_PUTS(cmd);
info->ampl_sp = value;
return FE_SUCCESS;
break;
// Frequency
case UWAVE_OUT_FREQ: // set Frequency in GHz
sprintf(cmd, "FREQ %f\n",value);
if (info->uWave_in_settings.detailed_msg == 2) // debug only
cm_msg(MINFO, "uWave_set", "uWave_set: cmd: %s", cmd);
BD_PUTS(cmd);
info->freq_sp = value;
return FE_SUCCESS;
break;
// Toggle on/off
case UWAVE_OUT_ONOFF: // output on=1, off=0
sprintf(cmd, "OUTP %d\n",(int)value);
if (info->uWave_in_settings.detailed_msg == 2) // debug only
cm_msg(MINFO, "uWave_set", "uWave_set: cmd: %s", cmd);
BD_PUTS(cmd);
info->onoff_sp = (int)value;
return FE_SUCCESS;
break;
default:
return FE_SUCCESS;
break;
}
if (info->uWave_in_settings.detailed_msg > 0)
cm_msg(MINFO, "uWave_set", "uWave_set: cmd: %s", cmd);
ss_sleep(100);
return FE_SUCCESS;
}
/*----------------------------------------------------------------------------------*/
/*!
* <p>get a value from the uWave.</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 uWave_get(UWAVE_INFO *info, INT channel, float *pvalue)
{
int status;
int chan;
char cmd[128], rcv[128];
DWORD nowtime, difftime;
// float fval, fval1;
// error timeout facility
nowtime = ss_time();
difftime = nowtime - info->lasterrtime;
if ( difftime > UWAVE_DELTA_TIME_ERROR ) {
info->errorcount = 0;
info->lasterrtime = ss_time();
}
// check if there was a startup error
if ( info->startup_error ) {
*pvalue = (float) UWAVE_INIT_ERROR;
ss_sleep(10); // to keep CPU load low when Run active
return FE_SUCCESS;
}
// check if there where too many reconnection failures
if (info->reconnection_failures > UWAVE_MAX_RECONNECTION_FAILURE) {
*pvalue = (float) UWAVE_READ_ERROR;
if (info->reconnection_failures == UWAVE_MAX_RECONNECTION_FAILURE+1) {
cm_msg(MERROR, "uWave_get", "too many reconnection failures, bailing out :-(");
info->reconnection_failures++;
}
return FE_SUCCESS;
}
// if disconnected, try to reconnect after a timeout of a timeout
if (!info->bd_connected) {
if (ss_time()-info->last_reconnect > UWAVE_RECONNECTION_TIMEOUT) { // timeout expired
if (info->uWave_in_settings.detailed_msg > 0)
cm_msg(MINFO, "uWave_get", "uWave: reconnection trial ...");
status = info->bd(CMD_INIT, info->hkey, &info->bd_info);
if (status != FE_SUCCESS) {
info->reconnection_failures++;
*pvalue = (float) UWAVE_READ_ERROR;
if (info->uWave_in_settings.detailed_msg > 0)
cm_msg(MINFO, "uWave_get", "uWave: reconnection attempted failed");
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;
if (info->uWave_in_settings.detailed_msg > 0)
cm_msg(MINFO, "uWave_get", "uWave: successfully reconnected");
}
} else { // timeout still running
ss_sleep(10);
return FE_SUCCESS;
}
}
// set the read command
switch (channel) {
case UWAVE_IN_PH1_RB: // check what is Phase 1
sprintf(cmd, "PH1?\n");
break;
case UWAVE_IN_PH2_RB: // check what is Phase 2
sprintf(cmd, "PH2?\n");
break;
case UWAVE_IN_AMPL_RB: // check what is the Amplitude
sprintf(cmd, "AMPL?\n");
break;
case UWAVE_IN_FREQ_RB: // check what is the Frequency
sprintf(cmd, "FREQ?\n");
break;
case UWAVE_IN_ONOFF_RB: // check what is the trigger level
sprintf(cmd, "OUTP?\n");
break;
default:
return FE_SUCCESS;
}
// The above commands return relative channel, value
// these need to be processed.
// send the command and read the response
BD_PUTS(cmd);
status = BD_GETS(rcv, sizeof(rcv), "\n", UWAVE_TIME_OUT);
if (info->uWave_in_settings.detailed_msg == 2) {// debug only
cm_msg(MINFO, "uWave_get", "uWave_get: cmd:\"%s\", %d, %s, status=%d", cmd, channel, rcv, status);
cm_msg(MINFO, "uWave_get", "uWave_get: cmd: %s", cmd);
cm_msg(MINFO, "uWave_get", "uWave_get: rdb: %s", rcv);
}
// check for any error
if (!status) { // error occured
*pvalue = (float) UWAVE_READ_ERROR;
status = UWAVE_READ_ERROR;
} else { // no error -> decode data
status = UWAVE_SUCCESS;
// interpret the response
switch (channel) {
case UWAVE_IN_PH1_RB: // read Phase 1
sscanf(rcv, "%f", pvalue);
break;
case UWAVE_IN_PH2_RB: // read Phase 2
sscanf(rcv, "%f", pvalue);
break;
case UWAVE_IN_AMPL_RB: // read Amplitude
sscanf(rcv, "%f", pvalue);
break;
case UWAVE_IN_FREQ_RB: // read Frequency
sscanf(rcv, "%f", pvalue);
break;
case UWAVE_IN_ONOFF_RB: // read On/Off state
sscanf(rcv, "%f", pvalue);
break;
default:
return FE_SUCCESS;
}
}
if (status != UWAVE_SUCCESS) {
if ( info->errorcount < UWAVE_MAX_ERROR ) {
if (info->uWave_in_settings.detailed_msg > 0)
cm_msg(MERROR, "uWave_get", "uWave communication or command error. Status = %d", status);
info->errorcount++;
}
info->readback_failure++;
if (info->readback_failure == UWAVE_MAX_READBACK_FAILURE) {
info->readback_failure = 0;
// try to disconnect and reconnect the bus driver
if ((ss_time()-info->last_reconnect > UWAVE_RECONNECTION_TIMEOUT) && info->bd_connected) {
info->bd(CMD_EXIT, info->bd_info); // disconnect bus driver
if (info->uWave_in_settings.detailed_msg > 0)
cm_msg(MINFO, "uWave_get", "uWave: try to disconnect and reconnect the bus driver");
info->last_reconnect = ss_time();
info->bd_connected = 0;
}
}
return FE_SUCCESS;
}
ss_sleep(1000);
return FE_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 uWave_in_get_label(UWAVE_INFO *info, INT channel, char *name)
{
strcpy(name, info->uWave_in_settings.name[channel]);
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 uWave_out_get_label(UWAVE_INFO *info, INT channel, char *name)
{
strcpy(name, info->uWave_out_settings.name[channel]);
return FE_SUCCESS;
}
/*---- device driver entry point ---------------------------------------------------*/
/*!
* <p>The device driver for the uWave is setup such that it can send TCP/IP commands
* to an RPi to control uWave properties.
*/
INT uWave_in(INT cmd, ...)
{
va_list argptr;
HNDLE hKey;
INT channel, status;
float *pvalue;
UWAVE_INFO *info;
char *name;
va_start(argptr, cmd);
status = FE_SUCCESS;
switch (cmd) {
case CMD_INIT:
{
hKey = va_arg(argptr, HNDLE);
UWAVE_INFO **pinfo = va_arg(argptr, UWAVE_INFO**);
channel = va_arg(argptr, INT);
va_arg(argptr, DWORD); // flags - currently not used
func_t *bd = va_arg(argptr, func_t*);
status = uWave_in_init(hKey, pinfo, channel, bd);
}
break;
case CMD_EXIT:
info = va_arg(argptr, UWAVE_INFO*);
status = uWave_exit(info);
break;
case CMD_GET:
info = va_arg(argptr, UWAVE_INFO*);
channel = va_arg(argptr, INT);
pvalue = va_arg(argptr, float*);
status = uWave_get(info, channel, pvalue);
break;
case CMD_GET_LABEL:
info = va_arg(argptr, UWAVE_INFO*);
channel = va_arg(argptr, INT);
name = va_arg(argptr, char *);
status = uWave_in_get_label(info, channel, name);
break;
default:
break;
}
va_end(argptr);
return status;
}
/************************************************************************/
INT uWave_out(INT cmd, ...)
{
va_list argptr;
HNDLE hKey;
INT channel, status;
float value;
UWAVE_INFO *info;
char *name;
va_start(argptr, cmd);
status = FE_SUCCESS;
switch (cmd) {
case CMD_INIT:
{
hKey = va_arg(argptr, HNDLE);
UWAVE_INFO **pinfo = va_arg(argptr, UWAVE_INFO**);
channel = va_arg(argptr, INT);
va_arg(argptr, DWORD); // flags - currently not used
func_t *bd = va_arg(argptr, func_t*);
status = uWave_out_init(hKey, pinfo, channel, bd);
}
break;
case CMD_SET:
info = va_arg(argptr, UWAVE_INFO*);
channel = va_arg(argptr, INT);
value = (float) va_arg(argptr, double);
status = uWave_set(info, channel, value);
break;
case CMD_GET_LABEL:
info = va_arg(argptr, UWAVE_INFO*);
channel = va_arg(argptr, INT);
name = va_arg(argptr, char *);
status = uWave_out_get_label(info, channel, name);
break;
default:
break;
}
va_end(argptr);
return status;
}
/*------------------------------------------------------------------*/