source: fact/FADctrl/FAD.cc@ 11103

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