source: fact/FADctrl/FAD.cc@ 10212

Last change on this file since 10212 was 10212, checked in by ogrimm, 11 years ago
Implemented new FAD data format
File size: 34.5 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) Boards[i]->Send(CMD_Trigger_S);
297 else Boards[i]->Send(0x2100 + (unsigned char) (1000.0/Num/12.5));
298 }
299 }
300 else if (Match(Parameter[1],"stop")) Boards[i]->Send(CMD_Trigger_S);
301 else if (Match(Parameter[1],"enable")) Boards[i]->Send(CMD_TRIGGERS_ON);
302 else if (Match(Parameter[1],"disable")) Boards[i]->Send(CMD_TRIGGERS_OFF);
303 else {
304 PrintUsage();
305 break;
306 }
307 }
308}
309
310//
311// Set DAC
312//
313void FAD::cmd_dac() {
314
315 int Value;
316 struct Range R = {0, NDAC-1};
317 unsigned short Buffer[2*NDAC] = {0};
318
319 // Check ranges
320 if(!ConvertToRange(Parameter[1], R)) {
321 PrintMessage("Error, DAC number out of range.\n");
322 return;
323 }
324
325 if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_DACVAL) {
326 PrintMessage("Error, DAC value out of range.\n");
327 return;
328 }
329
330 // Prepare command buffer
331 for (int i=R.Min; i<=R.Max; i++) {
332 Buffer[2*i] = htons(CMD_Write | (BADDR_DAC + i));
333 Buffer[2*i+1] = htons(Value);
334 }
335
336 // Send command buffer
337 for (unsigned int i=0; i<Boards.size(); i++) {
338 Boards[i]->Send(Buffer, sizeof(Buffer));
339 }
340}
341
342//
343// Set region-of-interest
344//
345void FAD::cmd_roi() {
346
347 int Value;
348 struct Range R = {0, NChips*NChannels-1};
349 unsigned short Buffer[2*NChips*NChannels] = {0};
350
351 // Check ranges
352 if (!ConvertToRange(Parameter[1], R)) {
353 PrintMessage("Error, ROI number out of range.\n");
354 return;
355 }
356
357 if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_ROIVAL) {
358 PrintMessage("Error, ROI value out of range.\n");
359 return;
360 }
361
362 // Prepare command buffer
363 for (int i=R.Min; i<=R.Max; i++) {
364 Buffer[2*i] = htons(CMD_Write | (BADDR_ROI + i));
365 Buffer[2*i+1] = htons(Value);
366 }
367
368 // Send command buffer
369 for (unsigned int i=0; i<Boards.size(); i++) {
370 Boards[i]->Send(Buffer, sizeof(Buffer));
371 }
372}
373
374//
375// Set addresses to value
376//
377void FAD::cmd_address() {
378
379 int Value;
380 struct Range R = {0, MAX_ADDR};
381 unsigned short Buffer[2*MAX_ADDR] = {0};
382
383 // Check ranges
384 if (!ConvertToRange(Parameter[1], R)) {
385 PrintMessage("Error, address out of range.\n");
386 return;
387 }
388
389 if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_VAL) {
390 PrintMessage("Error, value out of range.\n");
391 return;
392 }
393
394 // Prepare command buffer
395 for (int i=R.Min; i<=R.Max; i++) {
396 Buffer[2*i] = htons(CMD_Write | i);
397 Buffer[2*i+1] = htons(Value);
398 }
399
400 // Send command buffer
401 for (unsigned int i=0; i<Boards.size(); i++) {
402 Boards[i]->Send(Buffer, 2*(R.Max-R.Min+1)*sizeof(unsigned short));
403 }
404}
405
406//
407// Set ADC phase
408//
409void FAD::cmd_phase() {
410
411 int Value;
412
413 if (!ConvertToInt(Parameter[1], &Value)) {
414 PrintMessage("Error, illegal phase value\n");
415 return;
416 }
417
418 // Prepare command buffer
419 unsigned short *Buffer = new unsigned short [abs(Value)];
420 for (int i=0; i<abs(Value); i++) Buffer[i] = htons(CMD_PS_DO);
421
422 // Execute phase setting
423 for (unsigned int i=0; i<Boards.size(); i++) {
424 Boards[i]->Send(CMD_PS_RESET);
425 if (Value < 0) Boards[i]->Send(CMD_PS_DIRDEC);
426 else Boards[i]->Send(CMD_PS_DIRINC);
427 Boards[i]->Send(Buffer, abs(Value)*sizeof(unsigned short));
428 }
429
430 delete[] Buffer;
431}
432
433//
434// Send arbitrary data to board
435//
436void FAD::cmd_send() {
437
438 int Value;
439
440 if (!ConvertToInt(Parameter[1], &Value) || Value<0 || Value>MAX_VAL) {
441 PrintMessage("Error, illegal value\n");
442 return;
443 }
444
445 for (unsigned int i=0; i<Boards.size(); i++) Boards[i]->Send(Value);
446}
447
448/*
449// Set Domino mode
450void FAD::cmd_dmode() {
451 if (Match(Param[1],"continuous")) SetDOMINOMode(1);
452 else if (Match(Param[1],"single")) SetDOMINOMode(0);
453 else PrintUsage();
454}
455
456// Set Domino readout mode
457void FAD::cmd_rmode() {
458 if (Match(Param[1],"first")) SetDOMINOReadMode(0);
459 else if (Match(Param[1],"stop")) SetDOMINOReadMode(1);
460 else PrintUsage();
461}
462
463// Set Domino wave mode
464void FAD::cmd_wmode() {
465 if (Match(Param[1],"run")) SetDOMINOWaveMode(1);
466 else if (Match(Param[1],"stop")) SetDOMINOWaveMode(0);
467 else PrintUsage();
468}
469*/
470
471//
472// Start data run
473//
474void FAD::cmd_take() {
475
476 time_t Time = time(NULL);
477 struct tm *T = localtime(&Time);
478 char Filename[500];
479 double Temp;
480
481 // Set number of requested events
482 NumEventsRequested = atoi(Parameter[1].c_str());
483 NumEvents = 0;
484
485 // Open file with rwx right for owner and group, never overwrite file
486 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);
487
488 Datafile = open(Filename,O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
489 if(Datafile == -1) {
490 PrintMessage("Error: Could not open file \"%s\" (%s)\n", Filename, strerror(errno));
491 return;
492 }
493
494 // Check conditions for run of all active boards
495 float MaxTempDiff = atof(GetConfig("CalibTempDiffWarn").c_str());
496 float MaxFreqDiff = atof(GetConfig("CalibFreqDiffWarn").c_str());
497
498 for (unsigned int i=0; i<Boards.size(); i++) {
499 if (!Boards[i]->Active) continue;
500
501 if (Boards[i]->ACalib.Time == -1) PrintMessage("Warning: Amplitude calibration missing for board %d\n", i);
502 else {
503 Temp = 0;
504 for (unsigned int j=0; j<NTemp; j++) Temp += Boards[i]->GetStatus().Temp[j] / NTemp;
505 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);
506 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);
507 }
508 }
509
510 // Start run
511 Mode = datarun;
512 Message(INFO, "Starting run with %d events, filename '%s'", NumEventsRequested, Filename);
513}
514
515//
516// Amplitude calibration
517//
518void FAD::cmd_acalib() {
519
520 // Invalidate calibration?
521 if (Parameter.size() == 2 && Match(Parameter[1], "invalidate")) {
522 for (unsigned int i=0; i<Boards.size(); i++) Boards[i]->ACalib.Time = -1;
523 return;
524 }
525
526 // Read calibration data from file?
527 if (Parameter.size() == 2 && !ConvertToInt(Parameter[1], &NumEventsRequested)) {
528 FILE *File;
529 struct FADBoard::CalibData Data;
530
531 // Open file
532 if ((File = fopen(Parameter[1].c_str(), "r")) == NULL) {
533 PrintMessage("Error opening file '%s'\n", Parameter[1].c_str());
534 return;
535 }
536 // Read data and check if it applies to any board
537 while (fread(&Data, sizeof(Data), 1, File) == 1) {
538 for (unsigned int i=0; i<Boards.size(); i++) if (Data.DNA == Boards[i]->GetStatus().DNA) {
539 PrintMessage("Found calibration data for board %d (%s)\n", i, ctime(&Data.Time));
540 Boards[i]->ACalib = Data;
541 }
542 }
543 //Close file
544 if (fclose(File) != 0) PrintMessage("Could not close file '%s'\n", Parameter[1].c_str());
545 return;
546 } // Reading calibration from file
547
548 // Set number of events required for calibration
549 if (Parameter.size()==1 || !ConvertToInt(Parameter[1], &NumEventsRequested) || NumEventsRequested<=0) {
550 NumEventsRequested = DEFAULT_NUM_CALIB_EVENTS;
551 }
552
553 // Start calibration by setting mode
554 Mode = acalib;
555 Message(INFO, "Starting amplitude calibration run with 3x%d events", NumEventsRequested);
556}
557
558
559//
560// Print status
561//
562void FAD::cmd_status() {
563
564 // ==== Print board overview ====
565 if (Parameter.size() == 1) {
566
567 // Count active board
568 unsigned int Count = 0, Error = 0;
569 for (unsigned int i=0; i<Boards.size(); i++) {
570 if (Boards[i]->Active) Count++;
571 if (!Boards[i]->CommOK) Error++;
572 }
573
574 PrintMessage("\rTotal boards: %d (%d communication errors)\n", Boards.size(), Error);
575
576 // Print list of active boards
577 PrintMessage("Active are %d boards(s) ", Count);
578 for (unsigned int i=0; i<Boards.size(); i++) {
579 if (Boards[i]->Active) PrintMessage(" %d", i);
580 if (!Boards[i]->Active) PrintMessage("!");
581 }
582 PrintMessage("\n");
583
584 // Current mode
585 if (Mode == idle) PrintMessage("Current mode is IDLE\n");
586 else if (Mode == acalib) PrintMessage("Current mode is ACALIB (3x%d events)\n", NumEventsRequested);
587 else if (Mode == datarun) PrintMessage("Current mode is DATARUN (%d events requested, %d events taken)\n", NumEventsRequested, NumEvents);
588
589 return;
590 }
591
592 // ==== Print details for given range ====
593 struct Range R = {0, Boards.size()};
594
595 if (!ConvertToRange(Parameter[1], R)) {
596 PrintMessage("Error, out of range.\n");
597 return;
598 }
599
600 for (int i=0; i<(int) Boards.size(); i++) {
601 if (i<R.Min || i > R.Max) continue;
602
603 PrintMessage("BOARD #%d (%sactive) IP %s Communication %s\n", i, Boards[i]->Active ? "":"in", Boards[i]->Name, Boards[i]->CommOK ? "OK":"ERROR");
604
605 // Calibration information
606 if (Boards[i]->ACalib.Time == -1) PrintMessage("No amplitude calibration available\n");
607 else PrintMessage("Calibration data: Temperature %.1f Frequency %.2f Time %s" , Boards[i]->ACalib.Temp, Boards[i]->ACalib.Frequency, ctime(&Boards[i]->ACalib.Time));
608
609 // Status information
610 struct FADBoard::BoardStatus S = Boards[i]->GetStatus();
611
612 if (S.Update.tv_sec == -1) {
613 PrintMessage("No event received yet, no further status information available\n");
614 continue;
615 }
616 PrintMessage("Last event received %s", ctime(&S.Update.tv_sec));
617
618 // Board identification
619 PrintMessage("Board ID %d Firmware revision %d Serial %llx\n", S.BoardID, S.FirmwareRevision, S.DNA);
620 PrintMessage("Board time %d Event counter %d\n", S.BoardTime, S.EventCounter);
621
622 // Slow data
623 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]);
624 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] );
625 PrintMessage("Temperature %.2f %.2f %.2f %.2f", S.Temp[0], S.Temp[1], S.Temp[2], S.Temp[3]);
626
627 for (unsigned int j=0; j<NChips*NChannels; j++) {
628 if (j%NChannels == 0) PrintMessage("\nROI %2d-%2d: ", j, j+NChannels-1);
629 PrintMessage("%4d ", S.ROI[j/NChannels][j%NChannels]);
630 }
631 PrintMessage("\n");
632 /*
633 if (GetBoard(i)->GetStatusReg() & BIT_RUNNING)
634 PrintMessage(" Domino wave running\n");
635
636 if (GetBoard(i)->GetCtrlReg() & BIT_DMODE)
637 PrintMessage(" DMODE circular\n");
638 else
639 PrintMessage(" DMODE single shot\n");
640
641 }*/
642 } // for()
643}
644
645//
646// Adress FAD boards
647//
648void FAD::cmd_board() {
649
650 struct Range R = {0, Boards.size()};
651 int Mode = 0;
652
653 // Check if given boards should be enabled or disabled
654 if (Parameter[1].size() >= 1) {
655 if (Parameter[1][0] == '+') Mode = 1;
656 if (Parameter[1][0] == '-') Mode = -1;
657 }
658 if (Mode != 0) Parameter[1][0] = ' ';
659
660 // Evaluate given range
661 if (!ConvertToRange(Parameter[1], R)) {
662 PrintMessage("Error, out of range.\n");
663 return;
664 }
665
666 // Enable or disable boards
667 for (int i=0; i<(int) Boards.size(); i++) {
668 if (Mode == 0) Boards[i]->Active = false;
669 if (i >= R.Min && i <= R.Max) {
670 if (Mode != -1) Boards[i]->Active = true;
671 else Boards[i]->Active = false;
672 }
673 }
674}
675
676//
677// Reconnect to boards
678//
679void FAD::cmd_reconnect() {
680
681 struct Range R = {0, Boards.size()};
682
683 // Check ranges
684 if (!ConvertToRange(Parameter[1], R)) {
685 PrintMessage("Error, board number(s) out of range\n");
686 return;
687 }
688 PrintMessage("Note: Currently not protection against concurrent access in EventThread\n");
689
690 // Reinstantiate boards
691 for (int i=0; i<(int) Boards.size(); i++) if (i >= R.Min && i <= R.Max) {
692 if (pthread_kill(Boards[i]->Thread, 0) != ESRCH) {
693 PrintMessage("Event thread of board %d still running, cannot reconnect\n", i);
694 continue;
695 }
696
697 delete Boards[i];
698 Boards[i] = new class FADBoard(BoardList[i], PORT, this, i);
699 }
700
701 // Initialise boards if requested (use sendCommandNB() to avoid dead lock)
702 if (Parameter.size() != 3 || !Match(Parameter[2], "init")) return;
703
704 vector<string> Init = Tokenize(GetConfig("InitSequence"), ";");
705
706 for (unsigned int i=0; i<Init.size(); i++) {
707 DimClient::sendCommandNB(SERVER_NAME"/Command", (char *) Init[i].c_str());
708 }
709}
710
711//
712// Set DIM event update delay
713//
714void FAD::cmd_update() {
715
716 double Delay;
717
718 if (Parameter.size()==2 && ConvertToDouble(Parameter[1], &Delay) && Delay>=0) EventUpdateDelay = Delay;
719 else PrintUsage();
720}
721
722//
723// Print help
724//
725void FAD::cmd_help() {
726
727 char *Buffer;
728
729 for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
730 if (asprintf(&Buffer, "%s %s", CommandList[i].Name, CommandList[i].Parameters) == -1) {
731 PrintMessage("Error printing help, asprintf() failed\n");
732 break;
733 }
734 else {
735 if (strlen(Buffer) < 25) PrintMessage("%-25s%s\n", Buffer, CommandList[i].Help);
736 else PrintMessage("%s\n%-25s%s\n", Buffer, "", CommandList[i].Help);
737 }
738 free(Buffer);
739 }
740 PrintMessage(".<command> Execute shell command\n\n"
741 "Items in <> are mandatory, in [] optional, | indicates mutual exclusive.\n"
742 "Strings containing spaces have to be enclosed in \"double quotes\".\n"
743 "Ranges can be given as 'all', a single number or in the form 'a-b'.\n");
744}
745
746//
747// Stop current operation
748//
749void FAD::cmd_stop() {
750
751 static char Stop[] = "stop";
752
753 if (Mode == idle) {
754 PrintMessage("Nothing to stop\n");
755 return;
756 }
757
758 if (Mode == acalib) {
759 Mode = idle;
760 Message(INFO, "Mode set to IDLE");
761 }
762
763 if (Mode == datarun) {
764 // Inform event thread to stop run in case datarun active
765 if (write(Pipe[1], Stop, strlen(Stop)+1) == -1) {
766 Message(ERROR, "write() to Pipe[1] failed in FAD::cmd_cancel() (%s)", strerror(errno));
767 }
768 }
769
770 PrintMessage("Requested stopping of current operation\n");
771}
772
773//
774// Exit programm
775// SIGTERM makes readline() return (in case command came over network)
776//
777void FAD::cmd_exit() {
778
779 if (Mode != idle) cmd_stop();
780
781 ExitRequest = true;
782
783 // Wait to allow console input to arrive at readline()
784 usleep(10000);
785 pthread_kill(MainThread, SIGTERM);
786}
787
788
789// -----------------------------
790// ***** Other functions *****
791// -----------------------------
792
793//
794// DIM exit handler (overwriting handler in Evidence class)
795//
796void FAD::exitHandler(int Code) {
797
798 Message(INFO, "Exit handler called (DIM exit code %d)", Code);
799 cmd_exit();
800}
801
802//
803// Save amplitude calibration data to file
804//
805void FAD::SaveAmplitudeCalibration() {
806
807 PrintMessage("Amplitude calibration of all active boards finished, original ROI and DAC set\n");
808
809 // Open calibration data file
810 string Filename = string(getenv("HOME"))+"/FAD_ACal";
811 FILE *File = fopen(Filename.c_str(), "ab");
812
813 if (File == NULL) {
814 PrintMessage("Could not open calibration data file '%s'\n", Filename.c_str());
815 return;
816 }
817
818 // Write calibration information
819 for (unsigned int i=0; i<Boards.size(); i++) {
820 if (fwrite(&Boards[i]->ACalib, sizeof(Boards[i]->ACalib), 1, File) != 1) {
821 PrintMessage("Could not write to calibration file '%s'\n", Filename.c_str());
822 break;
823 }
824 }
825
826 // Close file
827 if (fclose(File) != 0) PrintMessage("Could not close calibration file '%s'\n", Filename.c_str());
828
829 PrintMessage("Appended amplitude calibration information to file '%s'\n", Filename.c_str());
830}
831
832//
833// Event thread (publishes/writes M0 format)
834//
835void FAD::EventThread() {
836
837 struct timeval Time, RunStart;
838 struct timeval LastUpdate;
839 struct FADBoard::BoardStatus S;
840 vector<unsigned long> EventNumbers(Boards.size());
841 vector<bool> AcalibDone(Boards.size());
842 double Temp;
843 string IDString;
844 char Buffer[100];
845 int Ret;
846 unsigned long long FileSize = 0;
847
848 gettimeofday(&LastUpdate, NULL);
849 RunStart = LastUpdate; // only to avoid 'uninitialized' warning from compiler
850
851 // Create DIM event data and number services
852 int EventSize = sizeof(RunHeader)+ Boards.size()*sizeof(BoardStructure)+sizeof(EventHeader) + Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int));
853 char *EventData = new char [EventSize];
854
855 memset(EventData, 0, EventSize);
856
857 DimService EventService(SERVER_NAME"/EventData", (char *) "C", NULL, 0);
858 DimService EventNumService(SERVER_NAME"/EventNumber", NumEvents);
859
860 // Calculate pointers to EventData array
861 RunHeader *RHeader = (RunHeader *) EventData;
862 BoardStructure **BStruct = new BoardStructure * [Boards.size()];
863 for (unsigned int i=0; i<Boards.size(); i++) BStruct[i] = ((BoardStructure *) (RHeader + 1)) + i;
864 EventHeader *EHeader = (EventHeader *) ((char *) (RHeader + 1) + Boards.size()*sizeof(BoardStructure));
865 int *TriggerCell = (int *) (EHeader + 1);
866 short *Data = (short *) (TriggerCell + NChips*Boards.size());
867
868 // M0 RunHeader
869 RHeader->DataFormat = DATA_FORMAT;
870 RHeader->RunHeaderSize = sizeof(RunHeader);
871 RHeader->EventHeaderSize = sizeof(EventHeader);
872 RHeader->BoardStructureSize = sizeof(BoardStructure);
873 RHeader->SoftwareRevision = atoi(REVISION) * (strchr(REVISION, 'M')==NULL ? 1:-1);
874 RHeader->Identification = 0;
875
876 RHeader->Type = 0; // Run type: 0=data, 1=pedestal, 3=test
877
878 RHeader->RunNumber = -1;
879 RHeader->FileNumber = 0;
880 snprintf(RHeader->Description, sizeof(RHeader->Description), "FADctrl");
881
882 RHeader->NBoards = Boards.size();
883 RHeader->NChips = NChips;
884 RHeader->NChannels = NChannels;
885 RHeader->Samples = NBins; // Always full pipeline
886 RHeader->Offset = 0;
887 RHeader->NBytes = sizeof(short);
888
889 // M0 EventHeader
890 EHeader->EventSize = Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int));
891
892 // Update loop
893 while (!ExitRequest) {
894 // Wait for data from TCP/IP reading threads
895 if ((Ret=read(Pipe[0], Buffer, sizeof(Buffer))) == -1) Message(FATAL, "read() from Pipe[0] failed in FAD::EventThread() (%s)", strerror(errno));
896
897 // Check if pipe closed
898 if (Ret == 0) break;
899
900 IDString = string(Buffer, Ret);
901
902 // If amplitude calibration mode, check if board finished procedure
903 if (Mode == acalib) {
904 bool Done = true;
905 for (unsigned int i=0; i<Boards.size(); i++) {
906 if (IDString.find(string("ACALIBDONE")+Boards[i]->Name) != string::npos) AcalibDone[i] = true;
907 if (!AcalibDone[i] && Boards[i]->Active && Boards[i]->CommOK) Done = false;
908 }
909 // Amplitude calibration finished?
910 if (Done) {
911 SaveAmplitudeCalibration();
912 Mode = idle;
913 Message(INFO, "Amplitude calibration done, mode set to IDLE");
914 }
915 }
916 else for (unsigned int i=0; i<Boards.size(); i++) AcalibDone[i] = false;
917
918 // Update run and event header with current time
919 gettimeofday(&Time, NULL);
920
921 RHeader->MagicNum = MAGICNUM_CLOSED;
922 RHeader->EndSecond = Time.tv_sec;
923 RHeader->EndMicrosecond = Time.tv_usec;
924
925 EHeader->Second = Time.tv_sec;
926 EHeader->Microsecond = Time.tv_usec;
927
928 // Close data file if requested or requested number of events reached
929 if((IDString.find("stop")!=string::npos || NumEvents==NumEventsRequested) && Mode==datarun) {
930
931 // Update run header
932 RHeader->Events = NumEvents;
933 RHeader->StartSecond = RunStart.tv_sec;
934 RHeader->StartMicrosecond = RunStart.tv_usec;
935
936 if (lseek(Datafile, 0, SEEK_SET) == -1) {
937 PrintMessage("Error: Could not rewind file to write updated run header (%s)\n", strerror(errno));
938 }
939 else if (write(Datafile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) {
940 PrintMessage("Error: Could not write updated run header (%s)\n", strerror(errno));
941 }
942
943 // Close data file and terminate run
944 if(close(Datafile) == -1) PrintMessage("Error: Could not close data file (%s)\n", strerror(errno));
945 else PrintMessage("Data file closed (size %.1f MByte).\n", FileSize/1024.0/1024);
946
947 Datafile = -1;
948 Mode = idle;
949 Message(INFO, "Data run ended, mode set to IDLE");
950 }
951
952 // These values might have changed while close file
953 RHeader->StartSecond = Time.tv_sec;
954 RHeader->StartMicrosecond = Time.tv_usec;
955 RHeader->Events = 1;
956
957 // Check all boards that have new data
958 for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
959 // Identify board
960 if (IDString.find(string("EVENT")+Boards[Brd]->Name) == string::npos) continue;
961
962 // Fill M0 BoardStructure
963 S = Boards[Brd]->GetStatus();
964 BStruct[Brd]->SerialNo = S.BoardID;
965 BStruct[Brd]->NomFreq = S.Frequency;
966 BStruct[Brd]->BoardTemp = 0;
967 for (unsigned int i=0; i<NTemp; i++) BStruct[Brd]->BoardTemp += S.Temp[i]/NTemp;
968 BStruct[Brd]->ScaleFactor = 1/2.048;
969
970 // Update event header with ID and Type of current board
971 EHeader->EventNumber = S.TriggerID;
972 EHeader->TriggerType = S.TriggerType;
973
974 // Register event number for data writing below
975 EventNumbers[Brd] = S.TriggerID;
976
977 // Write trigger cells
978 for(unsigned int i=0; i<NChips; i++) TriggerCell[Brd*NChips+i] = (int) S.TriggerCell[i];
979
980 // Write channel data (12 bit signed two's complement with out-of-range-bit and leading zeroes)
981 int Count = 0;
982 memset(Data, 0, Boards.size()*NChips*NChannels*NBins*sizeof(short));
983
984 Boards[Brd]->Lock();
985 for (unsigned int Chip=0; Chip<NChips; Chip++) for (unsigned int Chan=0; Chan<NChannels; Chan++) {
986 for (int i=0; i<S.ROI[Chip][Chan]; i++) {
987 if (Boards[Brd]->ACalib.Time == -1) Data[Count++] = Boards[Brd]->Data[Chip][Chan][i];
988 else {
989 Temp = (Boards[Brd]->Data[Chip][Chan][i] - Boards[Brd]->ACalib.Baseline[Chip][Chan][(i+S.TriggerCell[Chip])%NBins]);
990 Temp *= Boards[Brd]->ACalib.Gain[Chip][Chan][0]/Boards[Brd]->ACalib.Gain[Chip][Chan][(i+S.TriggerCell[Chip])%NBins];
991 //Temp -= Boards[Brd]->ACalib.Secondary[Chip][Chan][i];
992 Data[Count++] = (short) Temp;
993 }
994 }
995 Count += NBins - S.ROI[Chip][Chan];
996 }
997
998 // Inform TCP/IP thread that data has been processed
999 Boards[Brd]->Continue = true;
1000 Boards[Brd]->Unlock();
1001
1002 if ((Ret = pthread_cond_signal(&Boards[Brd]->CondVar)) != 0) {
1003 Message(FATAL, "pthread_cond_signal() failed (%s)", strerror(Ret));
1004 }
1005 } // Loop over boards
1006
1007 // Check if DIM service should be updated
1008 if ((Time.tv_sec-LastUpdate.tv_sec)*1e6 + Time.tv_usec-LastUpdate.tv_usec > EventUpdateDelay*1e6) {
1009 gettimeofday(&LastUpdate, NULL);
1010 EventService.updateService(EventData, EventSize);
1011 EventNumService.updateService();
1012 }
1013
1014 // ===== Data writing ===
1015
1016 if (Mode != datarun) continue;
1017
1018 // Check if all event numbers are the same
1019 bool Same = true;
1020 for (unsigned int i=0; i<Boards.size(); i++) {
1021 if (Boards[i]->Active && EventNumbers[i] != EventNumbers[0]) Same = false;
1022 }
1023 if (!Same) continue;
1024
1025 // Write also run header if this is the first event
1026 int Offset;
1027 if (NumEvents == 0) {
1028 RHeader->MagicNum = MAGICNUM_OPEN;
1029 RunStart = Time;
1030 Offset = 0;
1031 FileSize = 0;
1032 }
1033 else Offset = sizeof(RunHeader) + Boards.size()*sizeof(BoardStructure);
1034
1035 // Write data to file
1036 if(write(Datafile, EventData+Offset, EventSize-Offset) != (ssize_t) EventSize-Offset) {
1037 Message(ERROR, "Could not write all data to file, terminating run, setting mode to IDLE (%s)", strerror(errno));
1038
1039 // Close file if error
1040 if (close(Datafile) == -1) Message(ERROR, "Could not close data file (%s)", strerror(errno));
1041 Datafile = -1;
1042 Mode = idle;
1043 continue;
1044 }
1045
1046 NumEvents++;
1047 FileSize += EventSize-Offset;
1048 }
1049
1050 delete[] BStruct;
1051 delete[] EventData;
1052
1053}
1054
1055// Launch event thread inside class
1056void FAD::LaunchEventThread(class FAD *m) {
1057
1058 m->EventThread();
1059}
1060
1061
1062/*
1063// Set DOMINO mode
1064void FAD::SetDOMINOMode(int mode) {
1065
1066 for (int i=FirstBoard; i<=LastBoard; i++) {
1067 GetBoard(i)->SetDominoMode(mode==1 ? 1:0);
1068 PrintMessage("Domino mode of board %d switched to %s.\n",i,mode==1 ? "continuous":"single shot");
1069 }
1070}
1071
1072// Set DOMINO readout mode
1073void FAD::SetDOMINOReadMode(int mode) {
1074
1075 for (int i=FirstBoard; i<=LastBoard; i++) {
1076 GetBoard(i)->SetReadoutMode(mode);
1077 PrintMessage("Start readout of board %d from %s.\n",i,mode==0 ? "first bin":"stop position");
1078 }
1079}
1080
1081// Set DOMINO wave mode
1082void FAD::SetDOMINOWaveMode(int mode) {
1083
1084 for (int i=FirstBoard; i<=LastBoard; i++) {
1085 GetBoard(i)->SetDominoActive(mode);
1086 PrintMessage("Domino wave of board %d is %s during readout\n",i,mode==1 ? "running":"stopped");
1087 }
1088}
1089
1090*/
1091
1092
1093//
1094// Print usage text for command
1095//
1096void FAD::PrintUsage() {
1097
1098 for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
1099 if (Match(Parameter[0], CommandList[i].Name)) {
1100 PrintMessage("Usage: %s %s\n", CommandList[i].Name, CommandList[i].Parameters);
1101 }
1102 }
1103}
1104
1105//
1106// Print message to console
1107//
1108void FAD::PrintMessage(const char *Format, ...) {
1109
1110 static char Error[] = "vasprintf() failed in PrintMessage()";
1111 char *Text;
1112
1113 // Evaluate arguments
1114 va_list ArgumentPointer;
1115 va_start(ArgumentPointer, Format);
1116 if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
1117 va_end(ArgumentPointer);
1118
1119 if (strlen(Text) == 0) return;
1120
1121 // Print to console
1122 if (Text[strlen(Text)-1] == '\n') printf("\r"); // Overwrite prompt
1123 printf("%s", Text);
1124 fflush(stdout);
1125 if (Text[strlen(Text)-1]=='\n') rl_on_new_line(); // New prompt
1126
1127 // Send to DIM text service
1128 ConsoleOut->updateService(Text);
1129
1130 // Free old text
1131 if (ConsoleText != Error) free(ConsoleText);
1132 ConsoleText = Text;
1133}
1134
1135//
1136// Check if two strings match (min 1 character must match)
1137//
1138bool FAD::Match(string str, const char *cmd) {
1139
1140 return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true;
1141}
1142
1143//
1144// Conversion function from string to double, int or range
1145//
1146// Return false if conversion did not stop on whitespace or EOL character
1147bool FAD::ConvertToDouble(string String, double *Result) {
1148
1149 char *EndPointer;
1150
1151 *Result = strtod(String.c_str(), &EndPointer);
1152 if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
1153 return true;
1154}
1155
1156bool FAD::ConvertToInt(string String, int *Result) {
1157
1158 char *EndPointer;
1159
1160 *Result = (int) strtol(String.c_str(), &EndPointer, 0);
1161 if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
1162 return true;
1163}
1164
1165bool FAD::ConvertToRange(string String, struct FAD::Range &R) {
1166
1167 int N, M;
1168
1169 // Full range
1170 if (Match(String, "all")) return true;
1171
1172 // Single number
1173 if (ConvertToInt(String, &N)) {
1174 if (N>= R.Min && N<=R.Max) {
1175 R.Max = R.Min = N;
1176 return true;
1177 }
1178 return false;
1179 }
1180
1181 // Range a-b
1182 vector<string> V = EvidenceServer::Tokenize(String, "-");
1183 if (V.size()==2 && ConvertToInt(V[0], &N) && ConvertToInt(V[1], &M) && N>=R.Min && M<=R.Max) {
1184 R.Min = N;
1185 R.Max = M;
1186 return true;
1187 }
1188
1189 return false;
1190}
Note: See TracBrowser for help on using the repository browser.