source: hvcontrol/src/ProcessIO.cc@ 9621

Last change on this file since 9621 was 264, checked in by ogrimm, 14 years ago
Removed mutex locking in hvcontrol, use lockf() in Communicate()
File size: 18.8 KB
Line 
1
2/********************************************************************\
3
4 ProcessIO.cc
5
6 Main class processing user input
7
8 Sebastian Commichau, Sabrina Stark-Schneebeli, Oliver Grimm
9
10\********************************************************************/
11
12#include "ProcessIO.h"
13
14static const char* state_str[] = {"active", "stopped", "n.a."};
15
16// Branch table for command evaluation
17static const struct CL_Struct { const char *Name;
18 void (ProcessIO::*CommandPointer)();
19 unsigned int MinNumParameter;
20 const char *Parameters;
21 const char *Help;
22 } CommandList[] =
23 {{"board", &ProcessIO::cmd_board, 1, "<i>|<i j>|<all>" ,"Address board i, boards i-j or all boards or list boards"},
24 {"hv", &ProcessIO::cmd_hv, 2, "<id>|<ch>|<all> <v>", "Change bias of pixel or (all) chan. of active boards"},
25 {"status", &ProcessIO::cmd_status, 0, "[dac]", "Show status information (DAC values if requested)"},
26 {"config", &ProcessIO::cmd_config, 0, "", "Print configuration"},
27 {"load", &ProcessIO::cmd_load, 1, "<file>", "Load and set bias settings from file"},
28 {"save", &ProcessIO::cmd_save, 1, "<file>", "Save current bias settings to file"},
29 {"exit", &ProcessIO::cmd_exit, 0, "", "Exit program"},
30 {"rate", &ProcessIO::cmd_rate, 1, "<rate>", "Set status refresh rate in Hz"},
31 {"timeout", &ProcessIO::cmd_timeout, 1, "<time>", "Set timeout to return from read in seconds"},
32 {"reset", &ProcessIO::cmd_reset, 0, "", "Reset active bias boards"},
33 {"start", &ProcessIO::cmd_start, 0, "", "Start bias status monitor"},
34 {"stop", &ProcessIO::cmd_stop, 0, "", "Stop bias status monitor"},
35 {"uptime", &ProcessIO::cmd_uptime, 0, "", "Get program uptime"},
36 {"help", &ProcessIO::cmd_help, 0, "", "Print help"}};
37
38
39using namespace std;
40
41
42// Constructor
43ProcessIO::ProcessIO(): EvidenceServer(SERVER_NAME) {
44
45 // Get program start time
46 time (&StartTime);
47
48 // Initialize status variables
49 state = active;
50 ConsoleText = NULL;
51
52 NumHVBoards = 0;
53 FirstBoard = 0;
54 LastBoard = -1;
55
56 // DIM console service used in PrintMessage()
57 ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
58
59 // Get configuration data
60 vector<string> Boards = Tokenize(GetConfig("Boards"), " \t");
61 fTimeOut = atof(GetConfig("TimeOut").c_str());
62 fStatusRefreshRate = atof(GetConfig("StatusRefreshRate").c_str());
63 DACMin = atoi(GetConfig("DACMin").c_str());
64 DACMax = atoi(GetConfig("DACMax").c_str());
65 fHVCalibOffset = atof(GetConfig("HVCalibOffset").c_str());
66 fHVCalibSlope = atof(GetConfig("HVCalibSlope").c_str());
67 fHVMaxDiff = atoi(GetConfig("HVMaxDiff").c_str());
68
69 if (fStatusRefreshRate < MIN_RATE || fStatusRefreshRate > MAX_RATE) fStatusRefreshRate = 1;
70
71 // Open HV devices
72 fHVBoard = new HVBoard* [Boards.size()];
73 for (unsigned int i=0; i<Boards.size(); i++) {
74 fHVBoard[NumHVBoards] = new HVBoard(NumHVBoards, Boards[i], this);
75 if(fHVBoard[NumHVBoards]->fDescriptor >= 0) {
76 PrintMessage("Synchronized and reset board %d (%s)\n", NumHVBoards, fHVBoard[NumHVBoards]->BoardName);
77 NumHVBoards++;
78 }
79 else {
80 Message(WARN, "Failed to synchronize board %d (%s)", NumHVBoards, fHVBoard[NumHVBoards]->BoardName);
81 delete fHVBoard[NumHVBoards];
82 }
83 }
84 LastBoard = NumHVBoards-1;
85
86 // Create instances
87 calib = new HVCalib(this);
88 pm = new PixelMap(GetConfig("PixMapTable"));
89
90 // Install DIM command (after all initialized)
91 Command = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
92}
93
94
95// Destructor
96ProcessIO::~ProcessIO() {
97
98 delete Command;
99
100 for (int i=0; i<NumHVBoards; i++) delete fHVBoard[i];
101 delete[] fHVBoard;
102
103 delete pm;
104 delete calib;
105 delete ConsoleOut;
106 free(ConsoleText);
107}
108
109
110// Process user input
111void ProcessIO::CommandControl(char *Command) {
112
113 // Ignore empty commands
114 if (strlen(Command)==0) return;
115
116 // Shell command
117 if(Command[0]=='.') {
118 system(&(Command[1]));
119 return;
120 }
121
122 // Parse command into tokens
123 Parameter.clear();
124 char *Start;
125 while(true) {
126 while (isspace(*Command)) Command++; // Ignore initial white spaces
127 if(*Command=='\0') break;
128 if (*Command == '\"') {
129 Start = ++Command;
130 while(*Command!='\"' && *Command!='\0') Command++;
131 }
132 else {
133 Start = Command;
134 while(!isspace(*Command) && *Command!='\0') Command++;
135 }
136 if(*Command != '\0') *Command++ = '\0';
137 Parameter.push_back(Start);
138 }
139
140 // Search for command in command list
141 for(unsigned int CmdNumber=0; CmdNumber<sizeof(CommandList)/sizeof(CL_Struct); CmdNumber++) {
142 if (Match(Parameter[0], CommandList[CmdNumber].Name)) {
143 if(Parameter.size()-1 < CommandList[CmdNumber].MinNumParameter) {
144 PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters);
145 return;
146 }
147
148 // Jump to command function
149 (this->*CommandList[CmdNumber].CommandPointer)();
150 return;
151 }
152 }
153 PrintMessage("Unknown command '%s'\n", Parameter[0].c_str());
154}
155
156
157// Adress board
158void ProcessIO::cmd_board() {
159
160 // Print list of boards
161 if (Parameter.size() == 1) {
162 for (int i=0; i<NumHVBoards; i++) {
163 PrintMessage("Board %d: %s\n", fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName);
164 }
165 return;
166 }
167
168 //Select board(s)
169 if (Match(Parameter[1].c_str(), "all")) {
170 FirstBoard = 0;
171 LastBoard = NumHVBoards-1;
172 }
173 else if (Parameter.size()==2 && atoi(Parameter[1].c_str())>=0 && atoi(Parameter[1].c_str())<NumHVBoards) {
174 FirstBoard = atoi(Parameter[1].c_str());
175 LastBoard = FirstBoard;
176 }
177 else if (Parameter.size()==3 && atoi(Parameter[1].c_str())>=0 && atoi(Parameter[1].c_str())<NumHVBoards &&
178 atoi(Parameter[2].c_str())>0 && atoi(Parameter[2].c_str())<NumHVBoards) {
179 FirstBoard = atoi(Parameter[1].c_str());
180 LastBoard = atoi(Parameter[2].c_str());
181 }
182 else PrintMessage("Cannot address board(s), out of range.\n");
183}
184
185
186// Print configuration
187void ProcessIO::cmd_config() {
188
189 PrintMessage( " %d USB devices:\n", NumHVBoards);
190
191 for (int i=0; i<NumHVBoards; i++) PrintMessage(" Board %d: %s\n", i, fHVBoard[i]->BoardName);
192
193 PrintMessage( " StatusRefreshRate: %.2f Hz\n"
194 " DACMin value: %d\n"
195 " DACMax value: %d\n"
196 " HVCalibOffset : %f\n"
197 " HVCalibSlope : %f\n"
198 " HVMaxDiff : %u\n",
199 fStatusRefreshRate, DACMin,
200 DACMax, fHVCalibOffset, fHVCalibSlope, fHVMaxDiff);
201}
202
203// Print help
204void ProcessIO::cmd_help() {
205
206 char Buffer[MAX_COM_SIZE];
207 for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
208 snprintf(Buffer, sizeof(Buffer), "%s %s", CommandList[i].Name, CommandList[i].Parameters);
209 PrintMessage("%-28s%s\n", Buffer, CommandList[i].Help);
210 }
211
212 PrintMessage(".<command> Execute shell command\n\n"
213 "Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n");
214}
215
216// Set new bias voltage
217void ProcessIO::cmd_hv() {
218
219 int Int, SingleChannel;
220 unsigned int DACValue, Errors=0;
221 double Double;
222 bool SetDac;
223
224 // Interpretation as DAC value requested?
225 if (Parameter.size() == 4 && Match(Parameter[3], "dac")) SetDac = true;
226 else SetDac = false;
227
228 // Interprete first number (might be channel number)
229 if (!ConvertToInt(Parameter[1], &SingleChannel)) SingleChannel = -1;
230
231 // Loop over all boards
232 for (int i=FirstBoard; i<=LastBoard; i++) {
233 // Loop over all channels given as command parameter
234 for (unsigned int n=1; n<Parameter.size()-1; n+=2) {
235 // Loop over all channels
236 for (int j=0; j<NUM_CHAINS; j++) for (int k=0; k<NUM_CHANNELS; k++) {
237
238 // Current channel must be considered?
239 if (!Match(Parameter[1], "all") && !(i == (int) pm->Pixel_to_HVboard(Parameter[n]) &&
240 j == (int) pm->Pixel_to_HVchain(Parameter[n]) && k == (int) pm->Pixel_to_HVchannel(Parameter[n])) &&
241 !(j == SingleChannel/NUM_CHANNELS && k == SingleChannel%NUM_CHANNELS)) continue;
242
243 // Voltage change (number starts with + oder -) ignored if current DAC value is zero
244 if (isdigit(Parameter[n+1][0])==0 && fHVBoard[i]->HV[j][k] == 0) continue;
245
246 // Set new voltage/DAC value
247 if (!SetDac){
248 // Convert voltage value and check format
249 if (!ConvertToDouble(Parameter[n+1], &Double)) {
250 PrintMessage("Error: Wrong number format for voltage setting\n");
251 goto LeaveLoop;
252 }
253 // Adjust voltage and DAV value
254 if (isdigit(Parameter[n+1][0]) == 0) fHVBoard[i]->HVV[j][k] += Double;
255 else fHVBoard[i]->HVV[j][k] = Double;
256 DACValue = calib->HVToDAC(fHVBoard[i]->HVV[j][k], i, j, k);
257 }
258 else {
259 // Convert DAC value and check format
260 if (!ConvertToInt(Parameter[n+1], &Int)) {
261 PrintMessage("Error: Wrong number format for DAC voltage setting\n");
262 goto LeaveLoop;
263 }
264 // Adjust DAC value
265 if (isdigit(Parameter[n+1][0]) == 0) DACValue = fHVBoard[i]->HV[j][k] + Int;
266 else DACValue = Int;
267 }
268
269 // Set new voltage (if DAC value, update calibrated value)
270 if (!RampVoltage(DACValue, i, j, k)) Errors++;
271 if (SetDac) fHVBoard[i]->HVV[j][k] = calib->DACToHV(fHVBoard[i]->HV[j][k], i, j, k);
272 } // Channels and chains
273 } // Loop over command argument
274
275 // Update DIM service for this boar
276 LeaveLoop:
277 fHVBoard[i]->BiasVolt->updateService();
278 } // Boards
279
280 if (Errors > 0) PrintMessage("Errors on %d channel(s) occurred\n", Errors);
281}
282
283// Load bias settings from file
284void ProcessIO::cmd_load() {
285
286 char Buffer[MAX_COM_SIZE];
287 int NBoards = 0, Errors = 0, Chain, Channel;
288 unsigned int DACValue;
289 FILE *File;
290
291 if ((File=fopen(Parameter[1].c_str(), "r")) == NULL) {
292 PrintMessage("Error: Could not open file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
293 return;
294 }
295
296 while (fgets(Buffer, sizeof(Buffer), File) != NULL) {
297 for (int Board=0; Board<NumHVBoards; Board++) {
298 if (Match(fHVBoard[Board]->BoardName, Buffer)) {
299 PrintMessage("Found bias settings for board %d (%s)\n\r",fHVBoard[Board]->GetBoardNumber(), fHVBoard[Board]->BoardName);
300
301 Chain = 0; Channel = 0;
302 while (fscanf(File, "%u", &DACValue)==1 && Chain<NUM_CHAINS) {
303 if (!RampVoltage(DACValue, Board, Chain, Channel)) {
304 Errors++;
305 PrintMessage("Error: Could not ramp chain %d, channel %d\n", Chain, Channel);
306 }
307 else {
308 PrintMessage("Ramped chain %d, channel %d to %u (%.2f V) \r",
309 Chain,Channel,DACValue,calib->DACToHV(DACValue,Board,Chain,Channel));
310 }
311 fHVBoard[Board]->HVV[Chain][Channel] = calib->DACToHV(fHVBoard[Board]->HV[Chain][Channel], Board, Chain, Channel);
312
313 if(++Channel == NUM_CHANNELS) {
314 Chain++;
315 Channel = 0;
316 }
317 }
318
319 // Update DIM service
320 fHVBoard[Board]->BiasVolt->updateService();
321
322 if (ferror(File) != 0) {
323 PrintMessage("Error reading DAC value from file, terminating. (%s)\n",strerror(errno));
324 return;
325 }
326 else PrintMessage("\nFinished updating board\n");
327 NBoards++;
328 }
329 } // Loop over boards
330 } // while()
331
332 if (NBoards != NumHVBoards) {
333 PrintMessage("Warning: Could not load bias settings for all connected HV boards\n");
334 }
335 else if (Errors == 0) PrintMessage("Success: Read bias settings for all connected HV boards\n");
336 if (Errors != 0) PrintMessage("Warning: Errors on %d channel(s) occurred\n", Errors);
337
338 if (fclose(File) != 0) PrintMessage("Error: Could not close file '%s'\n",Parameter[1].c_str());
339}
340
341// Set status refresh rate
342void ProcessIO::cmd_rate() {
343
344 double Rate;
345
346 if (!ConvertToDouble(Parameter[1], &Rate)) {
347 PrintMessage("Error: Wrong number format\n");
348 return;
349 }
350
351 // Check limits
352 if (Rate<MIN_RATE || Rate>MAX_RATE) {
353 PrintMessage("Refresh rate out of range (min: %.2f Hz, max: %.2f Hz)\n", MIN_RATE, MAX_RATE);
354 return;
355 }
356
357 fStatusRefreshRate = Rate;
358 PrintMessage("Refresh rate set to %.2f Hz\n", fStatusRefreshRate);
359}
360
361// Reset
362void ProcessIO::cmd_reset() {
363
364 for (int i=FirstBoard; i<=LastBoard; i++) {
365 if (fHVBoard[i]->Reset() == 1) {
366 PrintMessage("Reset of board %d\n", fHVBoard[i]->GetBoardNumber());
367 PrintBoardStatus(i);
368 }
369 else PrintMessage("Error: Could not reset board %d\n",fHVBoard[i]->GetBoardNumber());
370 }
371}
372
373// Save bias settings of all boards
374void ProcessIO::cmd_save() {
375
376 FILE *File;
377 time_t time_now_secs;
378 struct tm *Time;
379
380 time(&time_now_secs);
381 Time = localtime(&time_now_secs);
382
383 if ((File = fopen(Parameter[1].c_str(), "w")) == NULL) {
384 PrintMessage("Error: Could not open file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
385 return;
386 }
387
388 fprintf(File,"********** Bias settings, %04d %02d %02d, %02d:%02d:%02d **********\n\n",
389 1900 + Time->tm_year, 1 + Time->tm_mon,
390 Time->tm_mday, Time->tm_hour, Time->tm_min, Time->tm_sec);
391
392 for (int i=0; i<NumHVBoards; i++) {
393 fprintf(File, "%s\n\n", fHVBoard[i]->BoardName);
394
395 for (int j=0; j<NUM_CHAINS; j++) {
396 for (int k=0; k<NUM_CHANNELS; k++) fprintf(File,"%5d ",fHVBoard[i]->HV[j][k]);
397 fprintf(File, "\n");
398 }
399 fprintf(File, "\n");
400 }
401
402 if (fclose(File) != 0) {
403 PrintMessage("Error: Could not close file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
404 }
405}
406
407// Start monitoring
408void ProcessIO::cmd_start() {
409
410 state = active;
411 pthread_kill(HVMonitor, SIGUSR1);
412 Message(INFO, "Status monitoring activated");
413}
414
415// Print status
416void ProcessIO::cmd_status() {
417
418 PrintMessage("\n Status monitor: %s\n", state_str[state]);
419 PrintMessage(" Status refresh rate [Hz]: %.2f\n", fStatusRefreshRate);
420 PrintMessage(" Total number of boards: %d\n", NumHVBoards);
421 PrintMessage(" Active boards: %d\n\n", LastBoard - FirstBoard + 1);
422
423 for (int i=FirstBoard; i<=LastBoard; i++) {
424 PrintMessage(" BOARD %d (%s) Wrap counter: %s (%d) Manual reset: %s\n Time-out: %.2f s Error count: %d\n\n",
425 fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName,
426 fHVBoard[i]->WrapOK ? "ok":"error", fHVBoard[i]->LastWrapCount,
427 fHVBoard[i]->ResetButton ? "yes" : "no", fHVBoard[i]->fTimeOut, fHVBoard[i]->ErrorCount);
428
429 for (int j=0; j<NUM_CHAINS; j++) {
430 PrintMessage(" CHAIN %d Over-current: %s\n", j, fHVBoard[i]->Overcurrent[j] ? "yes" : "no");
431 for (int l=0; l<NUM_CHANNELS; l++) {
432 if (l%8 == 0) PrintMessage("\r%3.1d: ", j*NUM_CHANNELS+l);
433 if (Parameter.size() == 2) PrintMessage("%5d ",fHVBoard[i]->HV[j][l]);
434 else PrintMessage("%#5.2f ",fHVBoard[i]->HVV[j][l]);
435 if (l%8 == 7) PrintMessage("\n");
436 }
437 }
438 }
439}
440
441// Stop monitoring
442void ProcessIO::cmd_stop() {
443
444 state = stopped;
445 pthread_kill(HVMonitor, SIGUSR1);
446 Message(INFO, "Status monitor stopped");
447}
448
449// Set timeout to return from read
450void ProcessIO::cmd_timeout() {
451
452 double Timeout;
453
454 if (!ConvertToDouble(Parameter[1], &Timeout)) {
455 PrintMessage("Error: Wrong number format\n");
456 return;
457 }
458
459 for (int i=0; i<NumHVBoards; i++) fHVBoard[i]->SetTimeOut(Timeout);
460 if (NumHVBoards > 0) PrintMessage("Timeout set to %.2f s for all boards\n", Timeout);
461}
462
463// Print uptime
464void ProcessIO::cmd_uptime() {
465
466 time_t ActualT;
467 time (&ActualT);
468 PrintMessage("%d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60);
469}
470
471// Exit program
472void ProcessIO::cmd_exit() {
473
474 ExitRequest = true;
475 pthread_kill(HVMonitor, SIGUSR1);
476}
477
478
479// Print message to screen and to DIM text service
480void ProcessIO::PrintMessage(const char *Format, ...) {
481
482 static char Error[] = "vasprintf() failed in PrintMessage()";
483 char *Text;
484
485 // Evaluate arguments
486 va_list ArgumentPointer;
487 va_start(ArgumentPointer, Format);
488 if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
489 va_end(ArgumentPointer);
490
491 // Print to console
492 if(strlen(Text)>0 && Text[strlen(Text)-1]=='\n') printf("\r%s%s", Text, Prompt); // New prompt
493 else printf("%s", Text);
494 fflush(stdout);
495
496 // Send to DIM text service
497 ConsoleOut->updateService(Text);
498
499 // Free old text
500 if (ConsoleText != Error) free(ConsoleText);
501 ConsoleText = Text;
502}
503
504
505// Ramp to new voltage with maximum step size given in fHVMaxDiff
506// No ramping when decreasing voltage
507bool ProcessIO::RampVoltage(unsigned int Target, int Board, int Chain, int Channel) {
508
509 while (fHVBoard[Board]->HV[Chain][Channel] != (int) Target) {
510 int Diff = Target - fHVBoard[Board]->HV[Chain][Channel];
511 if (Diff > (int) fHVMaxDiff) Diff = fHVMaxDiff;
512
513 if (fHVBoard[Board]->SetHV(Chain, Channel, fHVBoard[Board]->HV[Chain][Channel]+Diff) != 1) {
514 Message(ERROR, "Could not set bias of board %d, chain %d, channel %d. Skipping channel\n",fHVBoard[Board]->GetBoardNumber(),Chain,Channel);
515 return false;
516 }
517 }
518
519 return true;
520}
521
522
523// Check board status (ignore board if it has more than 10 read/write errors)
524void ProcessIO::Monitor() {
525
526 static bool Warned = false;
527
528 for (int i=0; i<NumHVBoards; i++) {
529 if (fHVBoard[i]->ErrorCount > 10) {
530 if (!Warned) {
531 Warned = true;
532 Message(WARN, "Warning: Board %d has many read/write errors, status monitor disabled", i);
533 }
534 continue;
535 }
536
537 if (fHVBoard[i]->GetStatus() != 1) {
538 Message(ERROR, "Error: Monitor could not read status of board %d", fHVBoard[i]->GetBoardNumber());
539 }
540
541 if (fHVBoard[i]->ResetButton) {
542 Message(INFO, "Manual reset of board %d", fHVBoard[i]->GetBoardNumber());
543 fHVBoard[i]->Reset();
544 }
545
546 if (!fHVBoard[i]->WrapOK) {
547 Message(ERROR, "Error: Wrap counter mismatch board %d",fHVBoard[i]->GetBoardNumber());
548 }
549
550 for (int j=0; j<NUM_CHAINS; j++) {
551 if (fHVBoard[i]->Overcurrent[j]) {
552 Message(WARN, "Warning: Overcurrent in chain %d of board %d, resetting board",j,fHVBoard[i]->GetBoardNumber());
553 fHVBoard[i]->Reset();
554 }
555 }
556 }
557}
558
559
560
561// Print current board status
562void ProcessIO::PrintBoardStatus(int i) {
563
564 PrintMessage("Status board %d (%s): MR %s OC0 %s OC1 %s OC2 %s OC3 %s WC %s (%d)\n",
565 fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName,
566 fHVBoard[i]->ResetButton ? "yes" : "no",
567 fHVBoard[i]->Overcurrent[0] ? "yes" : "no",
568 fHVBoard[i]->Overcurrent[1] ? "yes" : "no",
569 fHVBoard[i]->Overcurrent[2] ? "yes" : "no",
570 fHVBoard[i]->Overcurrent[3] ? "yes" : "no",
571 fHVBoard[i]->WrapOK ? "ok":"error", fHVBoard[i]->LastWrapCount);
572}
573
574
575// Command handling
576void ProcessIO::commandHandler() {
577
578 if ((getCommand()==Command) && (*(Command->getString()+Command->getSize()-1)=='\0')) {
579 CommandControl(Command->getString());
580 }
581}
582
583// Check if two strings match (min 1 character must match)
584bool Match(string str, const char *cmd) {
585 return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true;
586}
587
588
589// Convert string to double
590// Returns false if conversion did not stop on whitespace or EOL character
591bool ConvertToDouble(string String, double *Result) {
592
593 char *EndPointer;
594
595 *Result = strtod(String.c_str(), &EndPointer);
596 if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
597 return true;
598}
599
600
601// Convert string to int
602// Returns false if conversion did not stop on whitespace or EOL character
603bool ConvertToInt(string String, int *Result) {
604
605 char *EndPointer;
606
607 *Result = (int) strtol(String.c_str(), &EndPointer, 0);
608 if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
609 return true;
610}
Note: See TracBrowser for help on using the repository browser.