source: fact/FADctrl/FAD.cc @ 10813

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