source: fact/FADctrl/FAD.cc @ 10164

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