Back Midas Rome Roody Rootana
  Midas DAQ System  Not logged in ELOG logo
Entry  14 Jan 2004, Razvan Stefan Gornea, , Access to hardware in the MIDAS framework 
    Reply  14 Jan 2004, Stefan Ritt, , Access to hardware in the MIDAS framework 
       Reply  16 Jan 2004, Razvan Stefan Gornea, , Access to hardware in the MIDAS framework frontend.cmeterdev.c
          Reply  17 Jan 2004, Stefan Ritt, , Access to hardware in the MIDAS framework 
Message ID: 7     Entry time: 16 Jan 2004     In reply to: 6     Reply to this: 8
Author: Razvan Stefan Gornea 
Topic:  
Subject: Access to hardware in the MIDAS framework 
The multimeter device is indeed to simple to use MIDAS but I am just trying 
it as a learning experience. The DAQ system to develop involves VME crates 
and general purpose I/O boards. The slow control part, especially accessing 
the I/O boards seem to me more complex then the VME access. I want to 
understand very well the "correct" way of using the MIDAS slow control 
framework before starting the project.

I chose the second method and created a meterdev.c driver (essentially a 
copy of the nulldev.c) where I changed the init. function and the get 
function. I am not sending a "INIT ..." string because for this device it 
is useless. In the get function I send a "D" and read my string. I changed 
the frontend of the example to have a new driver list (in the first try I 
eliminated the Output device but the ODB got corrupted, I guess the class 
multi needs to have defined output channels). The output channel is linked 
with nulldev and null (I guess this is like if they would not be present).

The result is strange because the get function is called all the time very 
fast (much faster then the 9 seconds as set in the equipment) and even 
before starting the run (I just put the flag RO_RUNNING).

Thanks for any help
Attachment 1: frontend.c  2 kB  | Hide | Hide all
//********************************************************************************************
//
// Name:		frontend.c
// Created by:	Razvan Stefan Gornea
//
// Contents:	Slow Control frontend for a portable multimeter
//
// Log:			2004-01-15 14:22
//				Writing down initial code.
//
//********************************************************************************************

#include <stdio.h>
#include "midas.h"
#include "class/multi.h"
#include "device/nulldev.h"
#include "meterdev.h"
#include "bus/null.h"
#include "bus/rs232.h"

// globals variables

// frontend name
char *frontend_name = "Slow Control";
// frontend file name
char *frontend_file_name = __FILE__;
// frontend loop
BOOL frontend_call_loop = FALSE;
// frontend display refresh
INT display_period = 1000;
// maximum event size in bytes
INT max_event_size = 10000;
// maximum event size for fragments in bytes
INT max_event_size_frag = 5*1024*1024;
// buffer size in bytes
INT event_buffer_size = 10*10000;

// equipment list

// device drivers
DEVICE_DRIVER multi_driver[] = {
	{"Input", meterdev, 1, rs232, DF_INPUT},
	{"Output", nulldev, 1, null, DF_OUTPUT},
	{""}
};
// equipment list
EQUIPMENT equipment[] = {
  { "Multimeter",         /* equipment name */
    11, 0,                /* event ID, trigger mask */
    "SYSTEM",             /* event buffer */
    EQ_SLOW,              /* equipment type */
    0,                    /* event source */
    "FIXED",              /* format */
    TRUE,                 /* enabled */
    RO_RUNNING,           /* read when running */
    9000,                 /* read every 9 sec */
    0,                    /* stop run after this event limit */
    0,                    /* number of sub events */
    1,                    /* log history every event */
    "", "", "",
    cd_multi_read,        /* readout routine */
    cd_multi,             /* class driver main routine */
    multi_driver,         /* device driver list */
    NULL,                 /* init string */
  },
  { "" }
};

// routines

INT  poll_event(INT source[], INT count, BOOL test) {return 1;};
INT  interrupt_configure(INT cmd, INT source[], PTYPE adr) {return 1;};
// frontend initialization
INT frontend_init()
{
  return CM_SUCCESS;
}
// frontend exit
INT frontend_exit()
{
  return CM_SUCCESS;
}
// frontend loop
INT frontend_loop()
{
  return CM_SUCCESS;
}
// begin of run
INT begin_of_run(INT run_number, char *error)
{
  return CM_SUCCESS;
}
// end of run
INT end_of_run(INT run_number, char *error)
{
  return CM_SUCCESS;
}
// pause run
INT pause_run(INT run_number, char *error)
{
  return CM_SUCCESS;
}
// resume run
INT resume_run(INT run_number, char *error)
{
  return CM_SUCCESS;
}
Attachment 2: meterdev.c  6 kB  | Hide | Hide all
//********************************************************************************************
//
// Name:		meter.c
// Created by:	Razvan Stefan Gornea
//
// Contents:	Device driver for a portable multimeter
//
// Log:			2004-01-15 14:22
//				Writing down initial code.
//
//********************************************************************************************

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "midas.h"

// globals variables

#define DEFAULT_TIMEOUT 10000 // 10 secondes

typedef struct {
  int  address;
} METERDEV_SETTINGS;

#define METERDEV_SETTINGS_STR "\
Address = INT : 1\n\
"
typedef struct {
  METERDEV_SETTINGS meterdev_settings; // device settings
  float         *array; // data array
  INT           num_channels; // number of channels associated with this device
  INT (*bd)(INT cmd, ...); // bus driver entry function
  void *bd_info; // private settings and data related to the bus driver
  HNDLE hkey; // ODB key for bd_info structure
} METERDEV_INFO;

// routines

// initialization function: access the ODB to creates the settings, initializes the variables
// and calls the initialization function of the bus driver
INT meterdev_init(HNDLE hkey, void **pinfo, INT channels, INT (*bd)(INT cmd, ...))
{
	int status, size;
	HNDLE hDB, hkeydd;
	METERDEV_INFO *info;

	// allocate info structure
	info = calloc(1, sizeof(METERDEV_INFO));
	*pinfo = info;
	// get handle on current experiment ODB
	cm_get_experiment_database(&hDB, NULL);
	// create METERDEV settings record
	status = db_create_record(hDB, hkey, "DD", METERDEV_SETTINGS_STR); // force the ODB structure to match the METERDEV_SETTINGS C structure
	if (status != DB_SUCCESS) {
		return FE_ERR_ODB;
	}
	db_find_key(hDB, hkey, "DD", &hkeydd); // get handle on the DD key in the ODB associated with a certain equipment and device as pointed by the "hkey" handle
	size = sizeof(info->meterdev_settings); // get the size of the device settings structure
	db_get_record(hDB, hkeydd, &info->meterdev_settings, &size, 0); // load the device settings for the ODB, i.e. ontent of DD key
	// initialize the driver
	info->num_channels = channels; // define the nmber of channels
	info->array = calloc(channels, sizeof(float)); // allocate space for data
	info->bd = bd; // set handle on bus driver
	info->hkey = hkey; // set handle on the ODB key for the evice driver
	if (!bd) { // if handle invalid return error
		return FE_ERR_ODB;
	}
	// call the bus driver initialization routine
	status = info->bd(CMD_INIT, info->hkey, &info->bd_info);
	if (status != SUCCESS) {
		return status;
	}
	// initialization of device, something like ... 
	//BD_PUTS("init");
	// for this device no initialization string is needed ...

  return FE_SUCCESS;
}

// decomission function: free memory allocation(s) and close device(s)
INT meterdev_exit(METERDEV_INFO *info)
{
	// call EXIT function of bus driver, usually closes device
	info->bd(CMD_EXIT, info->bd_info);
	// free local variables
	if (info->array) {
		free(info->array);
	}
	free(info);
	
	return FE_SUCCESS;
}

// set channel value
INT meterdev_set(METERDEV_INFO *info, INT channel, float value)
{
	char str[80];
	
	// set channel to a specific value, something like ...
	sprintf(str, "SET %d %lf", channel, value);
	BD_PUTS(str);
	BD_GETS(str, sizeof(str), ">", DEFAULT_TIMEOUT);
	// simulate writing by storing value in local array, has to be removed in a real driver
	if (channel < info->num_channels) {
		info->array[channel] = value;
	}
	
	return FE_SUCCESS;
}

// set all channels values
INT meterdev_set_all(METERDEV_INFO *info, INT channels, float *value)
{
	int  i;
	char str[1000];

	// put here some optimized form of setting all channels simultaneously like ...
	strcpy(str, "SETALL ");
	for (i=0 ; i<min(info->num_channels, channels) ; i++) {
		sprintf(str+strlen(str), "%lf ", value[i]);
	}
	BD_PUTS(str);
	BD_GETS(str, sizeof(str), ">", DEFAULT_TIMEOUT);
	// simulate writing by storing values in local array
	for (i=0 ; i<min(info->num_channels, channels) ; i++) {
		info->array[i] = value[i];
	}
	
	return FE_SUCCESS;
}

// get channel value
INT meterdev_get(METERDEV_INFO *info, INT channel, float *pvalue)
{
	int  status, i;
	char str[80];
	char ascii_number[5];

	// read value from channel, something like ...
	//sprintf(str, "GET %d", channel);
	sprintf(str, "D"); // request data
	BD_PUTS(str);
	status = BD_GETS(str, sizeof(str), "\n", DEFAULT_TIMEOUT); // read until getting a cariage return or exit on timeout
	for (i = 0; i < 4; i++) { // transfer the number
		ascii_number[i] = str[i+4];
	}
	ascii_number[4] = '\0'; // end of string
	*pvalue = (float) atof(ascii_number); // convert from ASCII to float
	// simulate reading by copying set data from local array
	//if (channel < info->num_channels) {
	//	*pvalue = info->array[channel];
	//}
	//else {
	//	*pvalue = 0.f;
	//}
	
	return FE_SUCCESS;
}

// get all channels values
INT meterdev_get_all(METERDEV_INFO *info, INT channels, float *pvalue)
{
	// int  i;
	
	/* put here some optimized form of reading all channels. If the deviced
	does not support such a function, one can call nulldev_get() in a loop 
	strcpy(str, "GETALL");
	BD_PUTS(str);
	BD_GETS(str, sizeof(str), ">", DEFAULT_TIMEOUT);
	for (i=0 ; i<min(info->num_channels, channels) ; i++)
	pvalue[i] = atof(str+i*5); // extract individual values from reply
	*/
	/* simulate reading by copying set data from local array */
	//for (i=0 ; i<min(info->num_channels, channels) ; i++)
	//	pvalue[i] = info->array[i];
	
	return FE_SUCCESS;
}

// device driver entry point
INT meterdev(INT cmd, ...)
{
	va_list argptr;
	HNDLE   hKey;
	INT     channel, status;
	DWORD   flags;
	float   value, *pvalue;
	void    *info, *bd;
	va_start(argptr, cmd);
	status = FE_SUCCESS;
	
	switch (cmd) {
	case CMD_INIT:
		hKey = va_arg(argptr, HNDLE);
		info = va_arg(argptr, void *);
		channel = va_arg(argptr, INT);
		flags = va_arg(argptr, DWORD);
		bd = va_arg(argptr, void *);
		status = meterdev_init(hKey, info, channel, bd);
		break;
	case CMD_EXIT:
		info = va_arg(argptr, void *);
		status = meterdev_exit(info);
		break;
	case CMD_SET:
		info = va_arg(argptr, void *);
		channel = va_arg(argptr, INT);
		value = (float) va_arg(argptr, double); // floats are passed as double
		status = meterdev_set(info, channel, value);
		break;
	case CMD_SET_ALL:
		info = va_arg(argptr, void *);
		channel = va_arg(argptr, INT);
		pvalue   = (float *) va_arg(argptr, float *);
		status = meterdev_set_all(info, channel, pvalue);
		break;
	case CMD_GET:
		info = va_arg(argptr, void *);
		channel = va_arg(argptr, INT);
		pvalue  = va_arg(argptr, float*);
		status = meterdev_get(info, channel, pvalue);
		break;
	case CMD_GET_ALL:
		info = va_arg(argptr, void *);
		channel = va_arg(argptr, INT);
		pvalue  = va_arg(argptr, float*);
		status = meterdev_get_all(info, channel, pvalue);
		break;
	default:
		break;
	}
	va_end(argptr);
	
	return status;
}
ELOG V3.1.4-2e1708b5