source: fact/FADctrl/FAD.cc @ 10262

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