source: fact/FADctrl/FAD.cc@ 10927

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