source: fact/FADctrl/FAD.cc @ 10128

Last change on this file since 10128 was 10128, checked in by ogrimm, 9 years ago
Warning for data taking if amplitude calibration not done
File size: 35.4 KB
Line 
1/********************************************************************\
2
3  FAD.cc
4
5  Main class of FADCtrl
6
7
8 Comment 19/10/2010: It is assumed that boolean access is an atomic operation.
9     
10\********************************************************************/
11
12#include "FAD.h"
13using namespace std;
14
15static const struct CL_Struct { const char *Name;   
16              void (FAD::*CommandPointer)();
17                          bool NeedIdle;
18                          unsigned int MinNumParameter;
19                          const char *Parameters;
20                          const char *Help;
21  } CommandList[] = 
22  {{"board", &FAD::cmd_board, true, 1, "[+|-]<range>" ,"Activate or deactivate boards"},
23   {"status", &FAD::cmd_status, false, 0, "[range]", "Show board status information"},
24   {"domino", &FAD::cmd_domino, true, 1, "<on|off>", "Switch Domino wave"},
25   {"dwrite", &FAD::cmd_dwrite, true, 1, "<on|off>", "Set DWRITE"},
26   {"phase", &FAD::cmd_phase, true, 1, "<phase>", "Adjust ADC phase (in 'steps')"},
27   {"srclk", &FAD::cmd_srclk, true, 1, "<on|off>", "Set SRCLK"},
28   {"sclk", &FAD::cmd_sclk, true, 1, "<on|off>", "Set SCLK"},
29   {"trigger", &FAD::cmd_trigger, false, 0, "[n|cont [rate]|stop|enable|disable]", "Issue software triggers"},
30   {"roi", &FAD::cmd_roi, true, 2, "<channel range> <value>", "Set region-of-interest to value"},
31   {"dac", &FAD::cmd_dac, true, 2, "<range> <value>", "Set DAC numbers in range to value"},
32   {"address", &FAD::cmd_address, true, 2, "<range> <value>", "Set addresses in range to value"},
33   {"send", &FAD::cmd_send, true, 1, "<value>", "Send arbitrary data to board"},
34   {"take", &FAD::cmd_take, true, 1, "<n> <dir>", "Start run with n events, write to directory"},
35   {"acalib", &FAD::cmd_acalib, true, 0, "[n|invalidate|file]", "Perform or read amplitude calibration (n events)"},
36   //{"wmode", &FAD::cmd_wmode, 0, "<run|stop>", "Domino wave running or stopped during read out"},
37   //{"rmode", &FAD::cmd_rmode, 0, "<first|stop>", "Readout start at first bin or stop position (DRS4)"},
38   //{"dmode", &FAD::cmd_dmode, 0, "<single|continuous>", "Domino wave single shot or continuous"},
39   {"stop", &FAD::cmd_stop, false, 0, "", "Stop current operation/run"},
40   {"update", &FAD::cmd_update, false, 1, "<sec>", "Minimum delay between updates to DIM event service"},                 
41   {"socketmode", &FAD::cmd_socketmode, true, 1, "<com|daq>", "Choose which Sockets are used for data transmission"},             
42   {"exit", &FAD::cmd_exit, true, 0, "", "Exit program"},
43   {"help", &FAD::cmd_help, false, 0, "", "Print help"}};
44
45
46
47// ------------------------------------
48// *****  Constructor/Destructor  *****
49// ------------------------------------
50
51//
52// Constructor
53//
54FAD::FAD(): EvidenceServer(SERVER_NAME) {
55
56  // Initialization
57  ConsoleText = NULL;
58  MainThread = pthread_self();
59  Mode = idle;
60  Datafile = -1;
61  EventUpdateDelay = atof(GetConfig("EventUpdateDelay", "0.5").c_str());
62
63  // Create pipe for command execution and data exchange
64  if (pipe(CommandPipe) == -1) Message(FATAL, "pipe() failed in FAD::FAD() (%s)", strerror(errno));
65  if (pipe(Pipe) == -1) Message(FATAL, "pipe() failed in FAD::FAD() (%s)", strerror(errno));
66
67  // DIM console service used in PrintMessage()
68  ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
69
70  // Initialise configuration information (later non-blocking access in commandHandler())
71  GetConfig("CalibTempDiffWarn", "0");
72
73  // Construct boards
74  BoardList = Tokenize(GetConfig("BoardList"));
75
76  for (unsigned int i=0; i<BoardList.size(); i++) {
77    Boards.push_back(new class FADBoard(BoardList[i], 5000, this, i));
78
79        // Check if initialised OK
80        if (!Boards.back()->InitOK) {
81          Message(WARN, "Failed to initialize board %s", BoardList[i].c_str());
82          delete Boards.back();
83          Boards.pop_back();
84        }
85  }
86
87  // Create DIM event service thread
88  int Ret;
89  if ((Ret = pthread_create(&Thread, NULL, (void * (*)(void *)) LaunchEventThread, (void *) this)) != 0) {
90    Message(FATAL, "pthread_create() failed in FAD::FAD() (%s)", strerror(Ret));
91  }
92
93  // Create command handling thread
94  DimThread::start();
95
96  // Install DIM command (after all initialized)
97  Command = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
98}
99
100//
101// Destructor
102//
103FAD::~FAD() {
104
105  int Ret;
106
107  // Close pipe (will make read() on pipe in DIM service thread return)
108  if (close(Pipe[0]) == -1) Message(ERROR, "close() on Pipe[0] failed in FAD::~FAD() (%s)", strerror(errno));
109  if (close(Pipe[1]) == -1) Message(ERROR, "close() on Pipe[1] failed in FAD::~FAD() (%s)", strerror(errno));
110  if (close(CommandPipe[0]) == -1) Message(ERROR, "close() on CommandPipe[0] failed in FAD::~FAD() (%s)", strerror(errno));
111  if (close(CommandPipe[1]) == -1) Message(ERROR, "close() on CommandPipe[1] failed in FAD::~FAD() (%s)", strerror(errno));
112
113  // Wait for command and DIM service thread to quit
114  if ((Ret = pthread_join(Thread, NULL)) != 0) Message(ERROR, "pthread_join() failed in ~FAD() (%s)", strerror(Ret));
115
116  // Delete all boards (cancels threads automatically)
117  for (unsigned int i=0; i<Boards.size(); i++) delete Boards[i];
118
119  delete Command;
120  delete ConsoleOut;
121  free(ConsoleText);
122}
123
124// ------------------------------
125// *****  Command handling  *****
126// ------------------------------
127
128//
129// DIM command handler (non-blocking, otherwise a DIM rpc would dead-lock)
130//
131void FAD::commandHandler() {
132
133  // Ignore empty or illegal strings
134  if (getCommand()->getSize() == 0 || *(getCommand()->getString()+getCommand()->getSize()-1) != '\0' ||
135          strlen(getCommand()->getString()) == 0) return;
136
137  // Send command to command execution thread
138  if (write(CommandPipe[1], getCommand()->getData(), getCommand()->getSize()) == -1) {
139        Message(ERROR, "write() to CommandPipe[1] failed in class FAD::commandHandler() (%s)", strerror(errno));
140  }
141}
142
143//
144// Command execution thread
145//
146void FAD::threadHandler() {
147
148  char *Command, *Start, Buffer[1000];
149  int Ret;
150  unsigned int n;
151
152  while (!ExitRequest) {
153        if ((Ret=read(CommandPipe[0], Buffer, sizeof(Buffer))) == -1) Message(ERROR, "read() from CommandPipe[0] failed in FAD::threadHandler() (%s)", strerror(errno));
154
155        // Check if pipe closed
156        if (Ret == 0) break;
157
158        // Shell command
159        if (Buffer[0]=='.') {
160      system(&(Buffer[1]));
161      continue;
162        }
163
164        // Parse command into tokens
165        Parameter.clear();
166        Command = Buffer;
167        while(true) {
168      while (isspace(*Command)) Command++; // Ignore initial white spaces
169      if(*Command=='\0') break;
170      if (*Command == '\"') {
171                Start = ++Command;
172        while(*Command!='\"' && *Command!='\0') Command++;
173      }
174      else {
175                Start = Command;
176        while(!isspace(*Command) && *Command!='\0') Command++;
177      }
178      if(*Command != '\0') *Command++ = '\0';
179          Parameter.push_back(Start);
180        }
181
182        // Search for command in command list
183        for(n=0; n<sizeof(CommandList)/sizeof(CL_Struct); n++) {
184      if (Match(Parameter[0], CommandList[n].Name)) break;
185        }
186
187        // Command not found?   
188        if (n == sizeof(CommandList)/sizeof(CL_Struct)) {
189          PrintMessage("Unknown command '%s'\n", Parameter[0].c_str());
190          continue;
191        }
192
193        // Check if number of parameters
194    if(Parameter.size()-1 < CommandList[n].MinNumParameter) {
195          PrintMessage("Usage: %s %s\n", CommandList[n].Name, CommandList[n].Parameters);
196          continue;
197        }
198
199        // Check if idle mode required
200        if (CommandList[n].NeedIdle && Mode != idle) {
201          PrintMessage("Current mode is not idle ('stop' will stop current operation)\n");
202          continue;
203        }
204       
205        // Jump to command function
206        (this->*CommandList[n].CommandPointer)();
207  } // while()
208}
209
210//
211// DIM exit handler (overwriting handler in Evidence class)
212//
213void FAD::exitHandler(int Code) {
214
215  Message(INFO, "Exit handler called (DIM exit code %d)", Code);
216  pthread_kill(MainThread, SIGTERM);
217}
218
219//
220// Switch SRCLK
221//
222void FAD::cmd_srclk() {
223
224  for (unsigned int i=0; i<Boards.size(); i++) {
225        if (Match(Parameter[1],"on")) Boards[i]->Send(CMD_SRCLK_ON);
226        else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_SRCLK_OFF);
227  }
228
229  if (Match(Parameter[1],"on")) PrintMessage("SRCLK switched on for all active boards\n");
230  else if (Match(Parameter[1],"off")) PrintMessage("SRCLK switched off for all active boards\n");
231  else PrintUsage();
232} 
233
234//
235// Switch socket mode
236// "com" - command mode (only socket 0 is used)
237// "daq" - daq mode (only sockets 1 - 7 are used)
238//
239//      Note that socket 0 is always used to issue commands
240//
241void FAD::cmd_socketmode() {
242
243  for (unsigned int i=0; i<Boards.size(); i++) {
244        if (Match(Parameter[1],"com")) Boards[i]->Send(CMD_Stop);
245        else if (Match(Parameter[1],"daq")) Boards[i]->Send(CMD_Start);
246  }
247
248  if (Match(Parameter[1],"com")) PrintMessage("All active boards switched to command mode - socket 0\n");
249  else if (Match(Parameter[1],"daq")) PrintMessage("All active boards switched to DAQ mode - socket 1..7\n");
250  else PrintUsage();
251} 
252
253//
254// Switch SCLK
255//
256void FAD::cmd_sclk() {
257
258  for (unsigned int i=0; i<Boards.size(); i++) {
259        if (Match(Parameter[1],"on")) Boards[i]->Send(CMD_SCLK_ON);
260        else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_SCLK_OFF);
261  }
262
263  if (Match(Parameter[1],"on")) PrintMessage("SCLK switched on for all active boards\n");
264  else if (Match(Parameter[1],"off")) PrintMessage("SCLK switched off for all active boards\n");
265  else PrintUsage();
266} 
267
268//
269// Switch Domino wave
270//
271void FAD::cmd_domino() {
272
273  for (unsigned int i=0; i<Boards.size(); i++) {
274        if (Match(Parameter[1],"on")) Boards[i]->Send(CMD_DENABLE);
275        else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_DDISABLE);
276  }
277 
278  if (Match(Parameter[1],"on")) PrintMessage("Domino wave switched on for all active boards\n");
279  else if (Match(Parameter[1],"off")) PrintMessage("Domino wave switched off for all active boards\n");
280  else PrintUsage();
281} 
282
283//
284// Switch DWRITE
285//
286void FAD::cmd_dwrite() {
287
288  for (unsigned int i=0; i<Boards.size(); i++) {
289        if (Match(Parameter[1],"on")) Boards[i]->Send(CMD_DWRITE_RUN);
290        else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_DWRITE_STOP);
291  }
292
293  if (Match(Parameter[1],"on")) PrintMessage("DWRITE set high for all active boards\n");
294  else if (Match(Parameter[1],"off")) PrintMessage("DWRITE set low for all active boards\n");
295  else PrintUsage();
296} 
297
298//
299// Issue soft trigger
300//
301void FAD::cmd_trigger() {
302
303  int Num;
304
305  for (unsigned int i=0; i<Boards.size(); i++) {
306        if (Parameter.size() == 1) Boards[i]->Send(CMD_Trigger);
307        else if (ConvertToInt(Parameter[1], &Num)) {
308          for (int j=0; j<Num; j++) Boards[i]->Send(CMD_Trigger);
309        }
310        else if (Match(Parameter[1],"continuous")) {
311          Boards[i]->Send(CMD_Trigger_C);
312          if (Parameter.size() == 3 && ConvertToInt(Parameter[2], &Num)) {
313            if (Num == 0) Boards[i]->Send(CMD_Trigger_S);               
314                else Boards[i]->Send(0x2100 + (unsigned char) (1000.0/Num/12.5));
315          }
316        }
317        else if (Match(Parameter[1],"stop")) Boards[i]->Send(CMD_Trigger_S);
318        else if (Match(Parameter[1],"enable")) Boards[i]->Send(CMD_TRIGGERS_ON);
319        else if (Match(Parameter[1],"disable")) Boards[i]->Send(CMD_TRIGGERS_OFF);
320        else {
321          PrintUsage();
322          break;
323        }
324  }
325} 
326
327//
328// Set DAC
329//
330void FAD::cmd_dac() {
331
332  int Value;
333  struct Range R = {0, NDAC-1};
334  unsigned short Buffer[2*NDAC] = {0};
335
336  // Check ranges 
337  if(!ConvertToRange(Parameter[1], R)) {
338        PrintMessage("Error, DAC number out of range.\n");
339        return;
340  }
341 
342  if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_DACVAL) {
343        PrintMessage("Error, DAC value out of range.\n");
344        return;
345  }
346 
347  // Prepare command buffer
348  for (int i=R.Min; i<=R.Max; i++) {
349        Buffer[2*i] = htons(CMD_Write | (BADDR_DAC + i));
350        Buffer[2*i+1] = htons(Value);
351  }
352
353  // Send command buffer
354  for (unsigned int i=0; i<Boards.size(); i++) {
355        Boards[i]->Send(Buffer, sizeof(Buffer));
356  }
357} 
358
359//
360// Set region-of-interest
361//
362void FAD::cmd_roi() {
363
364  int Value;
365  struct Range R = {0, NChips*NChannels-1};
366  unsigned short Buffer[2*NChips*NChannels] = {0};
367
368  // Check ranges 
369  if (!ConvertToRange(Parameter[1], R)) {
370        PrintMessage("Error, ROI number out of range.\n");
371        return;
372  }
373 
374  if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_ROIVAL) {
375        PrintMessage("Error, ROI value out of range.\n");
376        return;
377  }
378 
379  // Prepare command buffer
380  for (int i=R.Min; i<=R.Max; i++) {
381        Buffer[2*i] = htons(CMD_Write | (BADDR_ROI + i));
382        Buffer[2*i+1] = htons(Value);
383  }
384 
385  // Send command buffer
386  for (unsigned int i=0; i<Boards.size(); i++) {
387        Boards[i]->Send(Buffer, sizeof(Buffer));
388  }
389} 
390
391//
392// Set addresses to value
393//
394void FAD::cmd_address() {
395
396  int Value;
397  struct Range R = {0, MAX_ADDR};
398  unsigned short Buffer[2*MAX_ADDR] = {0};
399
400  // Check ranges 
401  if (!ConvertToRange(Parameter[1], R)) {
402        PrintMessage("Error, address out of range.\n");
403        return;
404  }
405 
406  if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_VAL) {
407        PrintMessage("Error, value out of range.\n");
408        return;
409  }
410 
411  // Prepare command buffer
412  for (int i=R.Min; i<=R.Max; i++) {
413        Buffer[2*i] = htons(CMD_Write | i);
414        Buffer[2*i+1] = htons(Value);
415  }
416 
417  // Send command buffer
418  for (unsigned int i=0; i<Boards.size(); i++) {
419        Boards[i]->Send(Buffer, 2*(R.Max-R.Min+1)*sizeof(unsigned short));
420  }
421} 
422
423//
424// Set ADC phase
425//
426void FAD::cmd_phase() {
427
428  int Value;
429
430  if (!ConvertToInt(Parameter[1], &Value)) {
431        PrintMessage("Error, illegal phase value\n");
432        return;
433  }
434 
435  // Prepare command buffer
436  unsigned short *Buffer = new unsigned short [abs(Value)];
437  for (int i=0; i<abs(Value); i++) Buffer[i] = htons(CMD_PS_DO);
438 
439  // Execute phase setting
440  for (unsigned int i=0; i<Boards.size(); i++) {
441    Boards[i]->Send(CMD_PS_RESET);
442    if (Value < 0) Boards[i]->Send(CMD_PS_DIRDEC);
443        else Boards[i]->Send(CMD_PS_DIRINC);
444        Boards[i]->Send(Buffer, abs(Value)*sizeof(unsigned short));
445  }
446 
447  delete[] Buffer;
448} 
449
450//
451// Send arbitrary data to board
452//
453void FAD::cmd_send() {
454
455  int Value;
456
457  if (!ConvertToInt(Parameter[1], &Value) || Value<0 || Value>MAX_VAL) {
458        PrintMessage("Error, illegal value\n");
459        return;
460  }
461 
462  for (unsigned int i=0; i<Boards.size(); i++) Boards[i]->Send(Value);
463} 
464
465/*
466// Set Domino mode
467void FAD::cmd_dmode() {
468  if (Match(Param[1],"continuous")) SetDOMINOMode(1);
469  else if (Match(Param[1],"single")) SetDOMINOMode(0);
470  else PrintUsage();
471}
472
473// Set Domino readout mode
474void FAD::cmd_rmode() {
475  if (Match(Param[1],"first")) SetDOMINOReadMode(0);
476  else if (Match(Param[1],"stop")) SetDOMINOReadMode(1);
477  else PrintUsage();
478}
479
480// Set Domino wave mode
481void FAD::cmd_wmode() {
482  if (Match(Param[1],"run")) SetDOMINOWaveMode(1);
483  else if (Match(Param[1],"stop")) SetDOMINOWaveMode(0);
484  else PrintUsage();
485}
486*/
487
488//
489// Start data run
490//
491void FAD::cmd_take() {
492
493  time_t Time = time(NULL);
494  struct tm *T = localtime(&Time);
495  char Filename[500];
496  double Temp;
497
498  // Set number of requested events
499  NumEventsRequested = atoi(Parameter[1].c_str());
500  NumEvents = 0;
501
502  //  Open file with rwx right for owner and group, never overwrite file
503  snprintf(Filename, sizeof(Filename),"%s/%d%02d%02dT%02d%02d%02d.raw", Parameter[2].c_str(), T->tm_year+1900, T->tm_mon+1, T->tm_mday, T->tm_hour, T->tm_min, T->tm_sec);
504
505  Datafile = open(Filename,O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
506  if(Datafile == -1) {
507    PrintMessage("Error: Could not open file \"%s\" (%s)\n", Filename, strerror(errno));
508    return;
509  }
510
511  // Check conditions for run of all active boards
512  float MaxDiff = atof(GetConfig("CalibTempDiffWarn").c_str());
513
514  for (unsigned int i=0; i<Boards.size(); i++) {
515        if (!Boards[i]->Active) continue;
516
517        if (Boards[i]->ACalibTime == -1) PrintMessage("Warning: Amplitude calibration missing for board %d\n", i);
518        else {
519          Temp = 0;
520          for (unsigned int j=0; j<NTemp; j++) Temp += Boards[i]->GetStatus().Temp[j] / NTemp;
521          if (fabs(Boards[i]->ACalibTemp-Temp) > MaxDiff) PrintMessage("Warning: Amplitude calibration to current temperature difference larger than %.1f K for board %d\n", MaxDiff, i);
522        }
523  }
524
525  // Start run
526  Mode = datarun;
527  Message(INFO, "Starting run with %d events, filename '%s'", NumEventsRequested, Filename);
528}
529
530//
531// Amplitude calibration
532//
533void FAD::cmd_acalib() {
534
535  int Count;
536  FILE *File;
537  vector<string> Items;
538
539  // Invalidate calibration?
540  if (Parameter.size() == 2 && Match(Parameter[1], "invalidate")) {
541        for (unsigned int i=0; i<Boards.size(); i++) Boards[i]->ACalibTime = -1;
542        return;
543  }
544
545  // Read calibration data from file?
546  //if (Parameter.size() == 2 && !ConvertToInt(Parameter[1], &NumCalibEvents)) {
547  if (Parameter.size() == 2 && !ConvertToInt(Parameter[1], &NumEventsRequested)) {
548    // Open file
549        if ((File = fopen(Parameter[1].c_str(), "r")) == NULL) {
550          PrintMessage("Error opening file '%s'\n", Parameter[1].c_str());
551          return;
552        } 
553        PrintMessage("Reading amplitude calibration information from file '%s'\n", Parameter[1].c_str());
554
555        // Read file into buffer and close file
556        string Buffer;
557    while (feof(File)==0 && ferror(File)==0) Buffer.push_back((char) fgetc(File));
558        if (Buffer.size() > 0) Buffer = Buffer.substr(0, Buffer.size()-1);
559        if (fclose(File) != 0) PrintMessage("Could not close file '%s'\n", Parameter[1].c_str());
560
561        // Search for calibration data for boards
562        vector<string> Result = Tokenize(Buffer, "\n");
563
564        for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
565          for (unsigned int Line=0; Line<Result.size(); Line++) {
566            if (Result[Line].find(Boards[Brd]->Name) == string::npos) continue;
567
568                PrintMessage("Found calibration data for board '%s'\n", Boards[Brd]->Name);
569                Items = Tokenize(Result[Line]);
570               
571                // Check if correct number of items
572                if (Items.size() != NChips*NChannels*NBins*3 + 3) {
573                  PrintMessage("Error, data format invalid\n", Parameter[1].c_str());
574                  return;
575                }
576
577                // Extract data
578                Boards[Brd]->ACalibTemp = atof(Items[1].c_str());
579                Boards[Brd]->ACalibTime = atoi(Items[2].c_str());
580
581                Count = 3;
582                for (unsigned int i=0; i<NChips; i++) {
583                  for (unsigned int j=0; j<NChannels; j++) {
584                        for (unsigned int k=0; k<NBins; k++) {
585                          Boards[Brd]->Baseline[i][j][k] = atoi(Items[Count++].c_str());
586                          Boards[Brd]->Gain[i][j][k] = atof(Items[Count++].c_str());
587                          Boards[Brd]->SecondaryBaseline[i][j][k] = atof(Items[Count++].c_str());
588                        }
589                  }
590                }
591          }
592        } // Loop over boards
593
594        return;
595  } // Reading calibration from file
596
597  // Set number of events required for calibration
598  if (Parameter.size()==1 || !ConvertToInt(Parameter[1], &NumEventsRequested) || NumEventsRequested<=0) {
599        NumEventsRequested = DEFAULT_NUM_CALIB_EVENTS;
600  }
601
602  // Start calibration by setting mode
603  Mode = acalib;
604  Message(INFO, "Starting amplitude calibration run with 3x%d events", NumEventsRequested);
605}
606
607
608//
609// Print status
610//
611void FAD::cmd_status() {
612
613  // ==== Print board overview ====
614  if (Parameter.size() == 1) {
615
616        // Count active board
617        unsigned int Count = 0, Error = 0;
618        for (unsigned int i=0; i<Boards.size(); i++) {
619          if (Boards[i]->Active) Count++;
620          if (Boards[i]->CommError) Error++;
621        }         
622
623        PrintMessage("Total boards: %d    (%d communication errors)     Active boards: ", Boards.size(), Error);
624
625        // Print list of active boards
626        if (Count == 0) PrintMessage("none\n");
627        else if (Count == Boards.size()) PrintMessage("all\n");
628        else for (unsigned int i=0; i<Boards.size(); i++) {
629          if (Boards[i]->Active) PrintMessage(" %d", i);
630        }
631
632        // Current mode
633        if (Mode == idle) PrintMessage("Current mode is IDLE\n");
634        else if (Mode == acalib) PrintMessage("Current mode is ACALIB (3x%d events)\n", NumEventsRequested);
635        else if (Mode == datarun) PrintMessage("Current mode is DATARUN (%d events requested, %d events taken)\n", NumEventsRequested, NumEvents);
636
637        return;
638  }   
639
640  // ==== Print details for given range ====
641  struct Range R = {0, Boards.size()};
642
643  if (!ConvertToRange(Parameter[1], R)) {
644        PrintMessage("Error, out of range.\n");
645        return;
646  }
647
648  for (int i=0; i<(int) Boards.size(); i++) {
649        if (i<R.Min || i > R.Max) continue;
650
651        PrintMessage("BOARD #%d (%sactive)   IP %s   Communication %s\n", i, Boards[i]->Active ? "":"in", Boards[i]->Name, Boards[i]->CommError ? "ERROR":"OK");
652
653        // Calibration information
654        if (Boards[i]->ACalibTime == -1) PrintMessage("No amplitude calibration available\n");
655        else PrintMessage("Calibration temperature %.1f     Calibration time %s" , Boards[i]->ACalibTemp, ctime(&Boards[i]->ACalibTime));
656
657        // Status information
658        struct FADBoard::BoardStatus S = Boards[i]->GetStatus();
659
660        if (S.Update.tv_sec == -1) {
661          PrintMessage("No event received yet, no further status information available\n");
662          continue; 
663        }
664        PrintMessage("Last event received %s", ctime(&S.Update.tv_sec));
665
666        PrintMessage("Board ID %d               Firmware revision %d\n", S.BoardID, S.FirmwareRevision);
667        PrintMessage("DAC %d %d %d %d   %d %d %d %d\n", S.DAC[0], S.DAC[1], S.DAC[2], S.DAC[3], S.DAC[4], S.DAC[5], S.DAC[6], S.DAC[7] );
668        PrintMessage("Temperature %.2f %.2f %.2f %.2f", S.Temp[0], S.Temp[1], S.Temp[2], S.Temp[3]);
669
670        for (unsigned int j=0; j<NChips*NChannels; j++) {
671          if (j%NChannels == 0) PrintMessage("\nROI %2d-%2d: ", j, j+NChannels-1);
672          PrintMessage("%4d ", S.ROI[j/NChannels][j%NChannels]);
673        }
674        PrintMessage("\n");
675        /*PrintMessage("Serial %d, firmware %d\n"
676                    GetBoard(i)->GetBoardSerialNumber(),
677
678        if (GetBoard(i)->GetStatusReg() & BIT_RUNNING)
679          PrintMessage("   Domino wave running\n");
680
681        if (GetBoard(i)->GetCtrlReg() & BIT_DMODE)
682          PrintMessage("   DMODE circular\n");
683        else
684          PrintMessage("   DMODE single shot\n");
685 
686    }*/
687  } // for()
688}
689
690
691//
692// Adress FAD boards
693//
694void FAD::cmd_board() {
695 
696  struct Range R = {0, Boards.size()};
697  int Mode = 0;
698 
699  // Check if given boards should be enabled or disabled
700  if (Parameter[1].size() >= 1) {
701    if (Parameter[1][0] == '+') Mode = 1;
702    if (Parameter[1][0] == '-') Mode = -1;
703  }
704  if (Mode != 0) Parameter[1][0] = ' ';
705
706  // Evaluate given range
707  if (!ConvertToRange(Parameter[1], R)) {
708        PrintMessage("Error, out of range.\n");
709        return;
710  }
711 
712  // Enable or disable boards
713  for (int i=0; i<(int) Boards.size(); i++) {
714        if (Mode == 0) Boards[i]->Active = false;
715        if (i >= R.Min && i <= R.Max) {
716          if (Mode != -1) Boards[i]->Active = true;
717          else Boards[i]->Active = false;
718        }
719  } 
720} 
721
722//
723// Set DIM event update delay
724//
725void FAD::cmd_update() {
726
727  double Delay;
728 
729  if (Parameter.size()==2 && ConvertToDouble(Parameter[1], &Delay) && Delay>=0) EventUpdateDelay = Delay;
730  else PrintUsage();
731}
732
733//
734// Print help
735//
736void FAD::cmd_help() {
737
738  char *Buffer;
739
740  for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
741    if (asprintf(&Buffer, "%s %s", CommandList[i].Name, CommandList[i].Parameters) == -1) {
742          PrintMessage("Error printing help, asprintf() failed\n");
743          break;
744        }
745    else {
746        if (strlen(Buffer) < 25) PrintMessage("%-25s%s\n", Buffer, CommandList[i].Help);
747        else PrintMessage("%s\n%-25s%s\n", Buffer, "", CommandList[i].Help);
748        }
749        free(Buffer);
750  }
751  PrintMessage(".<command>                  Execute shell command\n\n"
752   "Items in <> are mandatory, in [] optional, | indicates mutual exclusive.\n"
753   "Strings containing spaces have to be enclosed in \"double quotes\".\n"
754   "Ranges can be given as 'all', a single number or in the form 'a-b'.\n"); 
755}
756
757//
758// Cancel current operation
759//
760void FAD::cmd_stop() {
761
762  static char Stop[] = "stop";
763
764  if (Mode == idle) {
765        PrintMessage("Nothing to stop\n");
766        return;
767  }
768 
769  if (Mode == acalib) {
770        Mode = idle;
771        Message(INFO, "Mode set to IDLE");
772  }
773
774  if (Mode == datarun) { 
775        // Inform event thread to stop run in case datarun active
776        if (write(Pipe[1], Stop, strlen(Stop)+1) == -1) {
777          Message(ERROR, "write() to Pipe[1] failed in FAD::cmd_cancel() (%s)", strerror(errno));
778        }
779  }
780 
781  PrintMessage("Requested stopping of current operation\n");
782}
783
784//
785// Exit programm
786// SIGTERM sets ExitRequest flag, and also makes readline() return (if command from DimCommand thread)
787//
788void FAD::cmd_exit() {
789
790  pthread_kill(MainThread, SIGTERM);
791}
792
793
794// -----------------------------
795// *****  Other functions  *****
796// -----------------------------
797
798//
799// Save amplitude calibration data to file
800//
801void FAD::SaveAmplitudeCalibration() {
802
803  PrintMessage("Amplitude calibration of all active boards finished, original ROI and DAC set\n");
804 
805  // Write calibration data to file
806  time_t Time = time(NULL);
807  struct tm *TimeInfo;
808  char Buffer[200];
809
810  // Generate filename
811  TimeInfo = localtime(&Time);
812  if (strftime(Buffer, sizeof(Buffer), "/FADcalib_%y-%m-%jT%X.txt", TimeInfo) == 0) {
813        PrintMessage("Could not generate calibration data file name, strftime() failed\n");
814        return;
815  }
816  string Filename = string(getenv("HOME"))+Buffer;
817
818  FILE *File = fopen(Filename.c_str(), "w");
819  if (File == NULL) {
820        PrintMessage("Could not open calibration data file '%s'\n", Filename.c_str());
821        return;
822  }
823 
824  // Fix: Should actually contain serial number!
825  for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
826        fprintf(File, "\n%s: %f %d ", Boards[Brd]->Name, Boards[Brd]->ACalibTemp, (int) Boards[Brd]->ACalibTime);
827        for (unsigned int i=0; i<NChips; i++) {
828          for (unsigned int j=0; j<NChannels; j++) {
829                for (unsigned int k=0; k<NBins; k++) {
830                  fprintf(File, "%d %lf %lf ", Boards[Brd]->Baseline[i][j][k], Boards[Brd]->Gain[i][j][k], Boards[Brd]->SecondaryBaseline[i][j][k]);
831                }
832          }
833        }
834  }
835
836  // Close file
837  if (fclose(File) != 0) {
838    PrintMessage("Could not close calibration file '%s'\n", Filename.c_str());
839  }
840 
841  PrintMessage("Wrote amplitude calibration information to file '%s'\n", Filename.c_str());
842}
843
844//
845// Event thread (publishes/writes M0 format)
846//
847void FAD::EventThread() {
848
849  struct timeval Time, RunStart;
850  struct timeval LastUpdate; 
851  struct FADBoard::BoardStatus S;
852  vector<unsigned long> EventNumbers(Boards.size());
853  vector<bool> AcalibDone(Boards.size());
854  double Temp;
855  string IDString;
856  char Buffer[100];
857  int Ret;
858  unsigned long long FileSize = 0;
859
860  gettimeofday(&LastUpdate, NULL);
861  RunStart = LastUpdate; // only to avoid 'uninitialized' warning from compiler
862 
863  // Create DIM event data and number services
864  int EventSize = sizeof(RunHeader)+ Boards.size()*sizeof(BoardStructure)+sizeof(EventHeader) + Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int));
865  char *EventData = new char [EventSize];
866
867  memset(EventData, 0, EventSize);
868
869  DimService EventService(SERVER_NAME"/EventData", (char *) "C", NULL, 0);
870  DimService EventNumService(SERVER_NAME"/EventNumber", NumEvents);
871
872  // Calculate pointers to EventData array
873  RunHeader *RHeader = (RunHeader *) EventData;
874  BoardStructure **BStruct = new BoardStructure * [Boards.size()];
875  for (unsigned int i=0; i<Boards.size(); i++) BStruct[i] = ((BoardStructure *) (RHeader + 1)) + i;
876  EventHeader *EHeader = (EventHeader *) ((char *) (RHeader + 1) + Boards.size()*sizeof(BoardStructure));
877  int *TriggerCell = (int *) (EHeader + 1);
878  short *Data = (short *) (TriggerCell + NChips*Boards.size());
879
880  // M0 RunHeader
881  RHeader->DataFormat = DATA_FORMAT;
882  RHeader->RunHeaderSize = sizeof(RunHeader);
883  RHeader->EventHeaderSize = sizeof(EventHeader);
884  RHeader->BoardStructureSize = sizeof(BoardStructure);
885  RHeader->SoftwareRevision = atoi(REVISION) * (strchr(REVISION, 'M')==NULL ? 1:-1);
886  RHeader->Identification = 0;
887
888  RHeader->Type = 0;                            // Run type: 0=data, 1=pedestal, 3=test
889
890  RHeader->RunNumber = -1;
891  RHeader->FileNumber = 0;
892  snprintf(RHeader->Description, sizeof(RHeader->Description), "FADctrl");       
893
894  RHeader->NBoards = Boards.size();
895  RHeader->NChips = NChips;
896  RHeader->NChannels = NChannels;
897  RHeader->Samples = NBins;                     // Always full pipeline
898  RHeader->Offset = 0;
899  RHeader->NBytes = sizeof(short);
900
901  // M0 EventHeader
902  EHeader->EventSize = Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int));
903
904  // Update loop
905  while (!ExitRequest) {
906    // Wait for data from TCP/IP reading threads
907    if ((Ret=read(Pipe[0], Buffer, sizeof(Buffer))) == -1) Message(FATAL, "read() from Pipe[0] failed in FAD::EventThread() (%s)", strerror(errno));
908
909        // Check if pipe closed
910        if (Ret == 0) break;
911       
912        IDString = string(Buffer, Ret);
913
914        // If amplitude calibration mode, check if board finished procedure
915        if (Mode == acalib) {
916          bool Done = true;
917          for (unsigned int i=0; i<Boards.size(); i++) {
918                if (IDString.find(string("ACALIBDONE")+Boards[i]->Name) != string::npos) AcalibDone[i] = true;
919                if (!AcalibDone[i]) Done = false;
920          }
921          // Amplitude calibration finished?
922          if (Done) {
923            SaveAmplitudeCalibration();
924                Mode = idle;
925                Message(INFO, "Amplitude calibration done, mode set to IDLE");
926          }
927        }
928        else for (unsigned int i=0; i<Boards.size(); i++) AcalibDone[i] = false;
929
930        // Update run and event header with current time
931        gettimeofday(&Time, NULL);
932
933        RHeader->MagicNum = MAGICNUM_CLOSED;
934        RHeader->EndSecond = Time.tv_sec;                       
935        RHeader->EndMicrosecond = Time.tv_usec;         
936
937        EHeader->Second = Time.tv_sec;
938        EHeader->Microsecond = Time.tv_usec;
939
940        // Close data file if requested or requested number of events reached
941        if((IDString.find("stop")!=string::npos || NumEvents==NumEventsRequested) && Mode==datarun) {
942
943          // Update run header 
944          RHeader->Events = NumEvents;
945          RHeader->StartSecond = RunStart.tv_sec;
946          RHeader->StartMicrosecond = RunStart.tv_usec;         
947
948          if (lseek(Datafile, 0, SEEK_SET) == -1) {
949            PrintMessage("Error: Could not rewind file to write updated run header (%s)\n", strerror(errno));
950          }
951          else if (write(Datafile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) {
952                PrintMessage("Error: Could not write updated run header (%s)\n", strerror(errno));
953          }
954
955          // Close data file and terminate run
956          if(close(Datafile) == -1) PrintMessage("Error: Could not close data file (%s)\n", strerror(errno));
957          else PrintMessage("Data file closed (size %.1f MByte).\n", FileSize/1024.0/1024);
958
959          Datafile = -1;
960          Mode = idle;
961          Message(INFO, "Data run ended, mode set to IDLE");     
962        }
963
964        // These values might have changed while close file
965        RHeader->StartSecond = Time.tv_sec;
966        RHeader->StartMicrosecond = Time.tv_usec;               
967    RHeader->Events = 1;
968
969        // Check all boards that have new data
970        for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
971          // Identify board
972          if (IDString.find(string("EVENT")+Boards[Brd]->Name) == string::npos) continue;
973
974          // Fill M0 BoardStructure             
975          S = Boards[Brd]->GetStatus();
976          BStruct[Brd]->SerialNo = S.BoardID;
977          BStruct[Brd]->NomFreq = 2;
978          BStruct[Brd]->BoardTemp = 0;       
979          for (unsigned int i=0; i<NTemp; i++) BStruct[Brd]->BoardTemp += S.Temp[i]/NTemp;
980          BStruct[Brd]->ScaleFactor = 1/2.048;     
981
982          // Update event header with ID and Type of current board
983          EHeader->EventNumber = S.TriggerID;
984          EHeader->TriggerType = S.TriggerType;
985
986          // Register event number for data writing below
987          EventNumbers[Brd] = S.TriggerID;
988
989          // Write trigger cells
990          for(unsigned int i=0; i<NChips; i++) TriggerCell[Brd*NChips+i] = (int) S.TriggerCell[i];
991
992          // Write channel data (12 bit signed two's complement with out-of-range-bit and leading zeroes)
993          int Count = 0;
994          memset(Data, 0, Boards.size()*NChips*NChannels*NBins*sizeof(short));
995
996          Boards[Brd]->Lock();
997          for (unsigned int Chip=0; Chip<NChips; Chip++) for (unsigned int Chan=0; Chan<NChannels; Chan++) {
998                for (int i=0; i<S.ROI[Chip][Chan]; i++) {
999                  if (Boards[Brd]->ACalibTime == -1) Data[Count++] = Boards[Brd]->Data[Chip][Chan][i];
1000                  else {
1001                    Temp = (Boards[Brd]->Data[Chip][Chan][i] - Boards[Brd]->Baseline[Chip][Chan][(i+S.TriggerCell[Chip])%NBins]);
1002                        Temp *= Boards[Brd]->Gain[Chip][Chan][0]/Boards[Brd]->Gain[Chip][Chan][(i+S.TriggerCell[Chip])%NBins];
1003                        //Temp -= Boards[Brd]->SecondaryBaseline[Chip][Chan][i];
1004                        Data[Count++] = (short) Temp;
1005                  }
1006                }
1007                Count += NBins - S.ROI[Chip][Chan];
1008          }
1009
1010          // Inform TCP/IP thread that data has been processed   
1011          Boards[Brd]->Continue = true;
1012          Boards[Brd]->Unlock();
1013
1014          if ((Ret = pthread_cond_signal(&Boards[Brd]->CondVar)) != 0) {
1015                Message(FATAL, "pthread_cond_signal() failed (%s)", strerror(Ret));
1016          }
1017        } // Loop over boards
1018
1019        // Check if DIM service should be updated
1020        if ((Time.tv_sec-LastUpdate.tv_sec)*1e6 + Time.tv_usec-LastUpdate.tv_usec > EventUpdateDelay*1e6) {
1021          gettimeofday(&LastUpdate, NULL);
1022          EventService.updateService(EventData, EventSize);
1023          EventNumService.updateService();
1024        }
1025       
1026        // ===== Data writing ===
1027       
1028        if (Mode != datarun) continue;
1029       
1030        // Check if all event numbers are the same
1031        bool Same = true;
1032        for (unsigned int i=0; i<Boards.size(); i++) {
1033          if (Boards[i]->Active && EventNumbers[i] != EventNumbers[0]) Same = false;
1034        }
1035        if (!Same) continue;
1036
1037        // Write also run header if this is the first event
1038        int Offset;     
1039        if (NumEvents == 0) {
1040          RHeader->MagicNum = MAGICNUM_OPEN;
1041          RunStart = Time;
1042          Offset = 0;
1043          FileSize = 0;
1044        }
1045        else Offset = sizeof(RunHeader) + Boards.size()*sizeof(BoardStructure);
1046       
1047        // Write data to file
1048        if(write(Datafile, EventData+Offset, EventSize-Offset) != (ssize_t) EventSize-Offset) {
1049          Message(ERROR, "Could not write all data to file, terminating run, setting mode to IDLE (%s)", strerror(errno));
1050
1051          // Close file if error
1052          if (close(Datafile) == -1) Message(ERROR, "Could not close data file (%s)", strerror(errno));
1053          Datafile = -1;
1054          Mode = idle;
1055          continue;
1056        }
1057       
1058        NumEvents++;
1059        FileSize += EventSize-Offset;
1060  }
1061
1062  delete[] BStruct;
1063  delete[] EventData;
1064
1065}
1066
1067// Launch event thread inside class
1068void FAD::LaunchEventThread(class FAD *m) {
1069
1070  m->EventThread();
1071}
1072
1073
1074/*
1075// Set DOMINO mode
1076void FAD::SetDOMINOMode(int mode) {
1077 
1078  for (int i=FirstBoard; i<=LastBoard; i++) {
1079    GetBoard(i)->SetDominoMode(mode==1 ? 1:0);
1080    PrintMessage("Domino mode of board %d switched to %s.\n",i,mode==1 ? "continuous":"single shot");
1081  }
1082}
1083
1084// Set DOMINO readout mode
1085void FAD::SetDOMINOReadMode(int mode) {
1086
1087  for (int i=FirstBoard; i<=LastBoard; i++) {
1088    GetBoard(i)->SetReadoutMode(mode);
1089    PrintMessage("Start readout of board %d from %s.\n",i,mode==0 ? "first bin":"stop position");
1090  }
1091}
1092
1093// Set DOMINO wave mode
1094void FAD::SetDOMINOWaveMode(int mode) {
1095
1096  for (int i=FirstBoard; i<=LastBoard; i++) {
1097    GetBoard(i)->SetDominoActive(mode);
1098    PrintMessage("Domino wave of board %d is %s during readout\n",i,mode==1 ? "running":"stopped");
1099  }
1100}
1101
1102*/
1103
1104
1105//
1106// Print usage text for command
1107//
1108void FAD::PrintUsage() {
1109
1110  for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
1111    if (Match(Parameter[0], CommandList[i].Name)) {
1112          PrintMessage("Usage: %s %s\n", CommandList[i].Name, CommandList[i].Parameters);
1113        }
1114  }
1115}
1116
1117//
1118// Print message to console
1119//
1120void FAD::PrintMessage(const char *Format, ...) {
1121
1122  static char Error[] = "vasprintf() failed in PrintMessage()";
1123  char *Text;
1124
1125  // Evaluate arguments
1126  va_list ArgumentPointer;
1127  va_start(ArgumentPointer, Format);
1128  if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
1129  va_end(ArgumentPointer);
1130 
1131  // Print to console
1132  printf("%s", Text);
1133  fflush(stdout);
1134  if (strlen(Text)>0 && Text[strlen(Text)-1]=='\n') rl_on_new_line(); // New prompt
1135
1136  // Send to DIM text service
1137  ConsoleOut->updateService(Text); 
1138
1139  // Free old text
1140  if (ConsoleText != Error) free(ConsoleText);
1141  ConsoleText = Text; 
1142}
1143
1144//
1145// Check if two strings match (min 1 character must match)
1146//
1147bool FAD::Match(string str, const char *cmd) {
1148
1149  return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true;
1150}
1151
1152//
1153// Conversion function from string to double, int or range
1154//
1155// Return false if conversion did not stop on whitespace or EOL character
1156bool FAD::ConvertToDouble(string String, double *Result) {
1157
1158  char *EndPointer;
1159 
1160  *Result = strtod(String.c_str(), &EndPointer);
1161  if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
1162  return true;
1163}
1164
1165bool FAD::ConvertToInt(string String, int *Result) {
1166
1167  char *EndPointer;
1168 
1169  *Result = (int) strtol(String.c_str(), &EndPointer, 0);
1170  if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
1171  return true;
1172}
1173
1174bool FAD::ConvertToRange(string String, struct FAD::Range &R) {
1175
1176  int N, M;
1177
1178  // Full range
1179  if (Match(String, "all")) return true;
1180
1181  // Single number
1182  if (ConvertToInt(String, &N)) {
1183        if (N>= R.Min && N<=R.Max) {
1184          R.Max = R.Min = N;
1185          return true;
1186        }
1187        return false;
1188  }
1189 
1190  // Range a-b
1191  vector<string> V = EvidenceServer::Tokenize(String, "-");
1192  if (V.size()==2 && ConvertToInt(V[0], &N) && ConvertToInt(V[1], &M) && N>=R.Min && M<=R.Max) {
1193        R.Min = N;
1194        R.Max = M;
1195        return true;
1196  }
1197 
1198  return false;
1199}
Note: See TracBrowser for help on using the repository browser.