source: fact/FADctrl/FAD.cc@ 14239

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