source: fact/FADctrl/FAD.cc @ 10117

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