source: fact/FADctrl/FAD.cc @ 10504

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