source: fact/FADctrl/FAD.cc@ 10187

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