source: fact/FADctrl/FAD.cc@ 11289

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