source: fact/FADctrl/FAD.cc@ 11259

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