source: fact/FADctrl/FAD.cc @ 10120

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