source: fact/FADctrl/FAD.cc@ 10969

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