source: fact/FADctrl/FAD.cc @ 10186

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