source: fact/FADctrl/FAD.cc@ 11939

Last change on this file since 11939 was 11385, checked in by ogrimm, 13 years ago
Board time published as DIM service, files written with permissions for OTHER
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|S_IROTH|S_IWOTH);
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.