Low-Energy Muon (LEM) Experiment  0.5.1
scs900.c
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------
2 
3  Name: scs900.c
4  Created by: Andreas Suter 2004/11/11
5 
6  Contents: MIDAS device driver for the SCS900 MSCB DAC card.
7 
8 ----------------------------------------------------------------------------*/
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <string.h>
14 
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 
21 #include "midas.h"
22 #include "msystem.h"
23 #include "mscb.h"
24 
25 #include "scs900.h"
26 
27 #include "lemSCWatchdog.h"
28 
29 // --------- to handle error messages ------------------------------
30 
31 #define SCS900_INIT_ERROR -2
32 #define SCS900_MSCB_DEBUG FALSE
33 
34 // ----------- SCS900 related infos ---------------------------------
35 #define SCS900_NOVARS 51
36 #define SCS900_DAC_OFFSET 8
37 
38 typedef struct {
41  INT scw_in_use;
43  char dd_name[NAME_LENGTH];
44  char port[NAME_LENGTH];
45  char pwd[NAME_LENGTH];
46  INT group_addr;
47  INT node_addr;
49  char err_log[4*NAME_LENGTH];
50  char adc_name[8][NAME_LENGTH];
51  char dac_name[8][NAME_LENGTH];
53 
55 #define SCS900_SETTINGS_STR "\
56 Detailed Messages = INT : 0\n\
57 SCW_IN_USE = INT : 0\n\
58 Read Period (sec) = INT : 1\n\
59 DD Name = STRING : [32] SCS900\n\
60 MSCB Port = STRING : [32] usb0\n\
61 MSCB Pwd = STRING : [32] \n\
62 Group Addr = INT : 1\n\
63 Node Addr = INT : 1\n\
64 Error Timeout = INT : 3600\n\
65 Error Log = STRING : [128] \n\
66 ADC Name = STRING[8] : \n\
67 [32] ADC0\n\
68 [32] ADC1\n\
69 [32] ADC2\n\
70 [32] ADC3\n\
71 [32] ADC4\n\
72 [32] ADC5\n\
73 [32] ADC6\n\
74 [32] ADC7\n\
75 DAC Name = STRING[8] : \n\
76 [32] DAC0\n\
77 [32] DAC1\n\
78 [32] DAC2\n\
79 [32] DAC3\n\
80 [32] DAC4\n\
81 [32] DAC5\n\
82 [32] DAC6\n\
83 [32] DAC7\n\
84 "
85 
87 #define SCS900_SCW_STR "\
88 Proc Name = STRING : [32]\n\
89 PID = INT : -1\n\
90 Log Name = STRING : [64]\n\
91 DD Name = STRING : [32] scs900\n\
92 Last Updated = DWORD : 0\n\
93 Timeout = DWORD : 180\n\
94 "
95 
97 typedef struct {
99  LEM_SC_WATCHDOG scw;
100  INT fd;
101  unsigned char dac_width;
102  char input_buffer[32];
103  float present_value[8];
104  float previous_value[8];
105  INT errcount;
107  DWORD lasterrtime;
108  DWORD read_timer;
109 } SCS900_INFO;
110 
112 
113 //---- device driver routines --------------------------------------
127 INT scs900_in_init(HNDLE hKey, void **pinfo, INT channels)
128 {
129  INT status, size;
130  HNDLE hDB, hkeydd;
131  MSCB_INFO node_info;
132  MSCB_INFO_VAR var_info;
133 
134  // allocate info structure
135  info = calloc(1, sizeof(SCS900_INFO));
136  *pinfo = info;
137 
138  cm_get_experiment_database(&hDB, NULL);
139 
140  // create SCS900 settings record
141  status = db_create_record(hDB, hKey, "DD/SCS900", SCS900_SETTINGS_STR);
142  if ((status != DB_SUCCESS) && (status != DB_OPEN_RECORD)) {
143  cm_msg(MERROR, "scs900_in_init", "scs900_in_init: Error creating SCS Settings record in ODB, status=%d", status);
144  cm_yield(0);
145  return FE_ERR_ODB;
146  }
147 
148  db_find_key(hDB, hKey, "DD/SCS900", &hkeydd);
149  size = sizeof(info->settings);
150  db_get_record(hDB, hkeydd, &info->settings, &size, 0);
151 
152  // create SCW settings record
153  status = db_create_record(hDB, hKey, "DD/SCW", SCS900_SCW_STR);
154  if ((status != DB_SUCCESS) && (status != DB_OPEN_RECORD)) {
155  cm_msg(MERROR, "scs900_in_init", "scs900_in_init: Error creating SCS900 SCW record in ODB, status=%d", status);
156  cm_yield(0);
157  return FE_ERR_ODB;
158  }
159 
160  // initialize driver
161  info->errcount = 0;
162  info->startup_error = 0;
163  info->lasterrtime = ss_time();
164  info->read_timer = 0;
165 
166  // initialize MSCB
167  info->fd = mscb_init(info->settings.port, sizeof(info->settings.port), info->settings.pwd, SCS900_MSCB_DEBUG);
168  if (info->fd == -1) {
169  cm_msg(MINFO, "scs900_init", "scs900_init: %s, Couldn't initialize MSCB port %s",
170  info->settings.dd_name, info->settings.port);
171  cm_yield(0);
172  info->startup_error = 1;
173  return FE_SUCCESS;
174  }
175  cm_msg(MINFO, "scs900_init", "scs900_init: %s: MSCB node %d initialized",
176  info->settings.dd_name, info->settings.node_addr);
177  cm_yield(0);
178 
179  // check if it is really a scs900
180  status=mscb_info(info->fd, info->settings.node_addr, &node_info);
181  if (status != MSCB_SUCCESS) {
182  cm_msg(MINFO, "scs900_init", "scs900_init: Couldn't get node info");
183  cm_yield(0);
184  info->startup_error = 1;
185  return FE_SUCCESS;
186  }
187  if (node_info.n_variables != SCS900_NOVARS) {
188  cm_msg(MINFO, "scs900_init", "scs900_init: %s: No of vars should be %d, found %d",
189  info->settings.dd_name, SCS900_NOVARS, node_info.n_variables);
190  cm_yield(0);
191  info->startup_error = 1;
192  return FE_SUCCESS;
193  }
194 
195  // get variable width of DAC
196  status=mscb_info_variable(info->fd, info->settings.node_addr, SCS900_DAC_OFFSET, &var_info);
197  if (status != MSCB_SUCCESS) {
198  cm_msg(MINFO, "scs900_init", "scs900_init: %s: Couldn't get DAC variable info",
199  info->settings.dd_name);
200  cm_yield(0);
201  info->startup_error = 1;
202  return FE_SUCCESS;
203  }
204  info->dac_width = var_info.width;
205 
206  cm_msg(MINFO, "scs900_init", "scs900_init: %s: MSCB node %d all necessary checks performed.",
207  info->settings.dd_name, info->settings.node_addr);
208  cm_yield(0);
209 
210  if (info->settings.scw_in_use) { // slowcontrol watchdog shall be used
211  // register with the slowcontrol watchdog -------------------------------------
212  // find scw record in DD
213  db_find_key(hDB, hKey, "DD/SCW", &hkeydd);
214  // get record
215  size = sizeof(info->scw);
216  db_get_record(hDB, hkeydd, &info->scw, &size, 0);
217  // fill watchdog structure
218  strncpy(info->scw.name, info->settings.dd_name, sizeof(info->scw.name));
219  info->scw.pid = ss_getpid();
220  info->scw.last_updated = ss_time();
221  // write info back into ODB
222  db_set_record(hDB, hkeydd, &info->scw, sizeof(info->scw), 0);
223 
224  // register with slowcontrol watchdog
225  status = lem_scw_init(&info->scw);
226  if (status != LEM_SCW_SUCCESS) {
227  cm_msg(MINFO, "scs900_in_init", "%s: Couldn't register with lem watchdog", info->settings.dd_name);
228  cm_yield(0);
229  }
230  }
231 
232  return FE_SUCCESS;
233 }
234 
235 /*----------------------------------------------------------------------------*/
248 INT scs900_out_init(HNDLE hKey, void **pinfo, INT channels)
249 {
250  HNDLE hDB;
251 
252  cm_get_experiment_database(&hDB, NULL);
253 
254  *pinfo = info;
255 
256  return FE_SUCCESS;
257 }
258 
259 /*----------------------------------------------------------------------------*/
269 {
270  // call EXIT function of MSCB driver, usually closes device
271  mscb_exit(info->fd);
272 
273  if (info->settings.scw_in_use && !info->startup_error) { // slowcontrol watchdog in use
274  // deregister from the slowcontrol watchdog
275  lem_scw_exit(&info->scw);
276  }
277 
278  free(info);
279  return FE_SUCCESS;
280 }
281 
282 /*----------------------------------------------------------------------------*/
292 INT scs900_set(SCS900_INFO *info, INT channel, float value)
293 {
294  INT status;
295  INT fd;
296  char tt[64], str[128];
297  time_t err_time;
298 
299  if (info->startup_error) {
300  ss_sleep(10); // to keep CPU load low when Run active
301  return FE_SUCCESS;
302  }
303 
304  // write data
305  status = mscb_write(info->fd, info->settings.node_addr,
306  (unsigned char) channel+SCS900_DAC_OFFSET,
307  (void *)&value, info->dac_width);
308 
309  if (status != MSCB_SUCCESS) { // error occurred
310  if (strlen(info->settings.err_log)>0) {
311  fd = open(info->settings.err_log, O_CREAT | O_WRONLY | O_APPEND, 0644);
312  if (fd>0) { // valid file descriptor
313  // get time
314  time(&err_time);
315  strcpy(tt, ctime(&err_time));
316  tt[strlen(tt)-1]='\0';
317 
318  sprintf(str, "[%s] scs900_set: %s, MSCB error %d", tt, info->settings.dd_name, status);
319  write(fd, str, strlen(str));
320 
321  close(fd);
322  }
323  }
324  info->errcount++;
325  }
326 
327  return FE_SUCCESS;
328 }
329 
330 /*----------------------------------------------------------------------------*/
341 INT scs900_get_in_label(SCS900_INFO *info, INT channel, char *name)
342 {
343  strcpy(name, info->settings.adc_name[channel]);
344  return FE_SUCCESS;
345 }
346 
347 /*----------------------------------------------------------------------------*/
358 INT scs900_get_out_label(SCS900_INFO *info, INT channel, char *name)
359 {
360  strcpy(name, info->settings.dac_name[channel]);
361  return FE_SUCCESS;
362 }
363 
364 /*-----------------------------------------------------------------------------*/
371 float scs900_mscb_to_float(char *data, int index)
372 {
373  int i;
374  char buff[4];
375  float result;
376 
377  for (i=0; i<4; i++)
378  buff[3-i] = data[index+i];
379 
380  result = *((float*) &buff);
381 
382  return result;
383 }
384 
385 /*-----------------------------------------------------------------------------*/
395 INT scs900_get(SCS900_INFO *info, INT channel, float *pvalue)
396 {
397  INT status, size;
398  DWORD nowtime, difftime;
399  INT fd, i;
400  char tt[64], str[128];
401  time_t err_time;
402 
403  // check if there was a startup error
404  if ( info->startup_error ) {
405  *pvalue = (float) SCS900_INIT_ERROR;
406  ss_sleep(10); // to keep CPU load low when Run active
407  return FE_SUCCESS;
408  }
409 
410  if (info->settings.scw_in_use) { // slowcontrol watchdog in use
411  // feed slowcontrol watchdog
412  info->scw.last_updated = ss_time();
413  lem_scw_update(&info->scw);
414  }
415 
416  // error timeout facility
417  nowtime = ss_time();
418  difftime = nowtime - info->lasterrtime;
419  if ( difftime > info->settings.err_timeout ) {
420  if (info->errcount > 0)
421  if (info->settings.detailed_msg)
422  cm_msg(MINFO, "scs900_get", "scs900_get: %s: %d errors occurred in the last %d sec",
423  info->settings.dd_name, info->errcount, info->settings.err_timeout);
424  info->errcount = 0;
425  info->lasterrtime = ss_time();
426  }
427 
428  // check if it is time to read the ADC values
429  if (nowtime - info->read_timer >= info->settings.read_timeout) {
430  // restart timer
431  info->read_timer = ss_time();
432  // read data
433  size = sizeof(info->input_buffer);
434  status = mscb_read_range(info->fd, info->settings.node_addr,
435  (unsigned char) 0, (unsigned char) 7,
436  &info->input_buffer, &size);
437 
438  if (status == MSCB_SUCCESS) { // got valid data
439  size = sizeof(float);
440  for (i=0; i<8; i++) {
441  info->present_value[i] = scs900_mscb_to_float(info->input_buffer, size*i);
442  info->previous_value[i] = info->present_value[i];
443  }
444  } else { // error getting data
445  for (i=0; i<8; i++)
446  info->present_value[i] = info->previous_value[i];
447 
448  if (strlen(info->settings.err_log)>0) {
449  fd = open(info->settings.err_log, O_CREAT | O_WRONLY | O_APPEND, 0644);
450 
451  if (fd>0) { // valid file descriptor
452  // get time
453  time(&err_time);
454  strcpy(tt, ctime(&err_time));
455  tt[strlen(tt)-1]='\0';
456 
457  sprintf(str, "[%s] scs900_set: %s, MSCB error %d\n", tt, info->settings.dd_name, status);
458  write(fd, str, strlen(str));
459 
460  close(fd);
461  }
462  }
463  info->errcount++;
464  }
465  }
466 
467  ss_sleep(10); // to keep CPU load low when Run active
468  *pvalue = info->present_value[channel];
469 
470  return FE_SUCCESS;
471 }
472 
473 /*---- device driver entry point -----------------------------------*/
474 
475 INT scs900_in(INT cmd, ...)
476 {
477 va_list argptr;
478 HNDLE hKey;
479 INT channel, status;
480 float *pvalue;
481 void *info;
482 char *name;
483 DWORD flags;
484 
485  va_start(argptr, cmd);
486  status = FE_SUCCESS;
487 
488  switch (cmd) {
489  case CMD_INIT:
490  hKey = va_arg(argptr, HNDLE);
491  info = va_arg(argptr, void *);
492  channel = va_arg(argptr, INT);
493  flags = va_arg(argptr, DWORD);
494  status = scs900_in_init(hKey, info, channel);
495  break;
496 
497  case CMD_EXIT:
498  info = va_arg(argptr, void *);
499  status = scs900_exit(info);
500  break;
501 
502  case CMD_GET:
503  info = va_arg(argptr, void *);
504  channel = va_arg(argptr, INT);
505  pvalue = va_arg(argptr, float*);
506  status = scs900_get(info, channel, pvalue);
507  break;
508 
509  case CMD_GET_LABEL:
510  info = va_arg(argptr, void *);
511  channel = va_arg(argptr, INT);
512  name = va_arg(argptr, char *);
513  status = scs900_get_in_label(info, channel, name);
514  break;
515 
516  default:
517  break;
518  }
519 
520  va_end(argptr);
521 
522  return status;
523 }
524 
525 /*------------------------------------------------------------------*/
526 
527 INT scs900_out(INT cmd, ...)
528 {
529 va_list argptr;
530 HNDLE hKey;
531 INT channel, status;
532 float value;
533 void *info;
534 char *name;
535 DWORD flags;
536 
537  va_start(argptr, cmd);
538  status = FE_SUCCESS;
539 
540  switch (cmd) {
541  case CMD_INIT:
542  hKey = va_arg(argptr, HNDLE);
543  info = va_arg(argptr, void *);
544  channel = va_arg(argptr, INT);
545  flags = va_arg(argptr, DWORD);
546  status = scs900_out_init(hKey, info, channel);
547  break;
548 
549  case CMD_SET:
550  info = va_arg(argptr, void *);
551  channel = va_arg(argptr, INT);
552  value = (float) va_arg(argptr, double);
553  status = scs900_set(info, channel, value);
554  break;
555 
556  case CMD_GET_LABEL:
557  info = va_arg(argptr, void *);
558  channel = va_arg(argptr, INT);
559  name = va_arg(argptr, char *);
560  status = scs900_get_out_label(info, channel, name);
561  break;
562 
563  default:
564  break;
565  }
566 
567  va_end(argptr);
568 
569  return status;
570 }
571 /*------------------------------------------------------------------*/
INT startup_error
initializer error tag, if set, scs900_get and scs900_set won&#39;t do anything
Definition: scs900.c:106
float present_value[8]
present ADC values
Definition: scs900.c:103
INT scs900_set(SCS900_INFO *info, INT channel, float value)
Definition: scs900.c:292
INFO info
Definition: vme_fe.c:206
INT fd
MSCB file desciptor.
Definition: scs900.c:100
INT group_addr
group address within the MSCB
Definition: scs900.c:46
char port[NAME_LENGTH]
MSCB port, e.g. /dev/parport0.
Definition: scs900.c:44
Stores all the parameters the device driver needs.
Definition: scs900.c:39
char dac_name[8][NAME_LENGTH]
Name of the DAC channels.
Definition: scs900.c:51
INT read_timeout
how often the data should be read in (sec)
Definition: scs900.c:42
LEM_SC_WATCHDOG scw
slowcontrol watchdog info structure
Definition: scs900.c:99
INT detailed_msg
flag indicating if detailed status/error messages are wanted
Definition: scs900.c:40
#define SCS900_DAC_OFFSET
offset for the DAC channels
Definition: scs900.c:36
INT scs900_in(INT cmd,...)
Definition: scs900.c:475
float previous_value[8]
previous ADC values
Definition: scs900.c:104
INT scs900_in_init(HNDLE hKey, void **pinfo, INT channels)
Definition: scs900.c:127
INT node_addr
node address within the MSCB
Definition: scs900.c:47
INT errcount
error counter in order not to flood the message queue
Definition: scs900.c:105
unsigned char dac_width
width of the DAC value
Definition: scs900.c:101
HNDLE hKey
Definition: write_summary.c:97
#define SCS900_INIT_ERROR
tag: initializing error
Definition: scs900.c:31
DWORD lasterrtime
last error time stamp
Definition: scs900.c:107
INT scs900_out(INT cmd,...)
Definition: scs900.c:527
INT scw_in_use
flag indicating if the slowcontrol watchdog shall be used
Definition: scs900.c:41
char err_log[4 *NAME_LENGTH]
name for error log file, if empty no log file will be written
Definition: scs900.c:49
INT scs900_out_init(HNDLE hKey, void **pinfo, INT channels)
Definition: scs900.c:248
float scs900_mscb_to_float(char *data, int index)
Definition: scs900.c:371
HNDLE hDB
Definition: write_summary.c:97
#define SCS900_SETTINGS_STR
Initializing string for the struct SCS900_SETTINGS.
Definition: scs900.c:55
INT scs900_get_in_label(SCS900_INFO *info, INT channel, char *name)
Definition: scs900.c:341
DWORD read_timer
timer to get the input data read
Definition: scs900.c:108
INT scs900_get(SCS900_INFO *info, INT channel, float *pvalue)
Definition: scs900.c:395
#define SCS900_NOVARS
number of variables of the SCS900
Definition: scs900.c:35
#define SCS900_MSCB_DEBUG
MSCB debug flag.
Definition: scs900.c:32
INT scs900_get_out_label(SCS900_INFO *info, INT channel, char *name)
Definition: scs900.c:358
INT scs900_exit(SCS900_INFO *info)
Definition: scs900.c:268
char input_buffer[32]
raw input buffer for the ADC values
Definition: scs900.c:102
#define SCS900_SCW_STR
defines the slowcontrol default watchdog info structure
Definition: scs900.c:87
char pwd[NAME_LENGTH]
MSCB password for the MSCB ethernet submaster.
Definition: scs900.c:45
char dd_name[NAME_LENGTH]
internal name for the DD
Definition: scs900.c:43
INT err_timeout
error report interval in sec
Definition: scs900.c:48
This structure contains private variables for the device driver.
Definition: scs900.c:97
SCS900_SETTINGS settings
stores the internal DD settings
Definition: scs900.c:98
char adc_name[8][NAME_LENGTH]
Name of the ADC channels.
Definition: scs900.c:50