Changeset 220


Ignore:
Timestamp:
Jun 7, 2010, 8:01:05 AM (10 years ago)
Author:
ogrimm
Message:
hv command allows array of voltages (for use by feedback)
Location:
hvcontrol
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • hvcontrol/History.txt

    r161 r220  
    3434                        server
    353529/1/2010       DAC value 0 is now equivilant to calibrated voltage value 0
     364/6/2010        Command 'hv' allows an array of pixel IDs/voltages (used by feedback)
  • hvcontrol/src/HV.cc

    r183 r220  
    1717HVBoard::HVBoard(int DeviceNumber, char *DeviceName, class ProcessIO *PIO) {
    1818   
    19   char Buffer[BUFFER_LENGTH];
     19  char *Buffer;
    2020  struct termios tio;
    2121 
     
    2727 
    2828  // Create DIM services
    29   snprintf(Buffer, sizeof(Buffer), SERVER_NAME"/NAME/ID%.2d", BoardNumber);
     29  if (asprintf(&Buffer, SERVER_NAME"/NAME/ID%.2d", BoardNumber) == -1) {
     30    m->PrintMessage(All, "asprintf() failed for DIM service name creation\n");
     31        return;
     32  }
    3033  Name = new DimService (Buffer, BoardName);
    31 
    32   snprintf(Buffer, sizeof(Buffer), SERVER_NAME"/VOLT/ID%.2d", BoardNumber);
     34  free(Buffer);
     35 
     36  if (asprintf(&Buffer, SERVER_NAME"/VOLT/ID%.2d", BoardNumber) == -1) {
     37    m->PrintMessage(All, "asprintf() failed for DIM service name creation\n");
     38        return;
     39  }
    3340  BiasVolt = new DimService (Buffer, "D", HVV, NUM_CHAINS*NUM_CHANNELS*sizeof(double));
     41  free(Buffer);
    3442
    3543  for (int i=0; i<NUM_CHAINS; i++) Overcurrent[i] = false;
     
    4250
    4351  // Open device (do not warn on non-existing device)
    44   snprintf(Buffer, BUFFER_LENGTH, "/dev/%s",DeviceName);
     52  if (asprintf(&Buffer, "/dev/%s",DeviceName) == -1) {
     53    m->PrintMessage(All, "asprintf() failed for device name creation\n");
     54        return;
     55  }
    4556  if ((fDescriptor = open(Buffer, O_RDWR|O_NOCTTY|O_NDELAY)) == -1) {
    4657    if(errno != 2) m->PrintMessage(All, "Error: Could not open device %d/%s (%s)\n", DeviceNumber,DeviceName, strerror(errno));
     58        free(Buffer);
    4759    return;
    4860  }
     61  free(Buffer);
    4962
    5063  // Get current serial port settings
  • hvcontrol/src/ProcessIO.cc

    r210 r220  
    1414static char* state_str[]    = {"active", "stopped", "n.a."};
    1515
    16 ProcessIO::ProcessIO():
    17             DimCommand((char *) SERVER_NAME"/Command", (char *) "C"),
    18             EvidenceServer(SERVER_NAME) {
     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        {"chain", &ProcessIO::cmd_chain, 1, "<i>|<i> <j>|<all>","Address chain i, chains i-j or all chains"},
     25        {"hv", &ProcessIO::cmd_hv, 2, "<id>|<ch>|<all> <v>", "Change bias of pixel, ch. or all ch. of active chain(s)/board(s)"},
     26        {"status", &ProcessIO::cmd_status, 0, "[dac]", "Show status information (DAC values if requested)"},
     27        {"config", &ProcessIO::cmd_config, 0, "", "Print configuration"},
     28        {"load", &ProcessIO::cmd_load, 1, "<file>", "Load and set bias settings from file"},
     29        {"save", &ProcessIO::cmd_save, 1, "<file>", "Save current bias settings to file"},
     30        {"exit", &ProcessIO::cmd_exit, 0, "", "Exit program"},
     31        {"rate", &ProcessIO::cmd_rate, 1, "<rate>", "Set status refresh rate in Hz"},
     32        {"timeout", &ProcessIO::cmd_timeout, 1, "<time>", "Set timeout to return from read in seconds"},
     33        {"reset", &ProcessIO::cmd_reset, 0, "", "Reset active bias board"},
     34        {"start", &ProcessIO::cmd_start, 0, "", "Start bias status monitor"},
     35        {"stop", &ProcessIO::cmd_stop, 0, "", "Stop bias status monitor"},
     36        {"uptime", &ProcessIO::cmd_uptime, 0, "", "Get program uptime"},
     37        {"help", &ProcessIO::cmd_help, 0, "", "Print help"}};
     38
     39
     40using namespace std;
     41
     42ProcessIO::ProcessIO(): EvidenceServer(SERVER_NAME) {
    1943
    2044  // Get program start time
     
    3054  FirstChain  = 0;
    3155  LastChain   = NUM_CHAINS-1;
    32 
    33   UpdateDelay = 0.0;
    3456
    3557  // Get configuration data
     
    6284  }
    6385  LastBoard = NumHVBoards-1;
    64  
     86
    6587  // Create instances
    6688  calib  = new HVCalib(this);
    67   pm     = new PixelMap(fPixMapTable); 
     89  pm     = new PixelMap(fPixMapTable);
     90 
     91  // Install DIM command (after all initialized)
     92  Command = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
    6893}
    6994
     
    7196ProcessIO::~ProcessIO() {
    7297 
     98  delete Command;
     99
    73100  for (int i=0; i<NumHVBoards; i++) delete fHVBoard[i];
    74101  delete[] fHVBoard;
     
    81108void ProcessIO::CommandControl(char *Command) {
    82109
    83   if (strlen(Command)==0) return;  // Ignore empty commands
    84 
    85   if(Command[0]=='.') {   // Shell command
     110  // Ignore empty commands
     111  if (strlen(Command)==0) return;
     112
     113  // Shell command
     114  if(Command[0]=='.') {
    86115    system(&(Command[1]));
    87116    return;
    88117  }
    89118
    90   for(int i=0; i<MAX_NUM_TOKEN; i++) Param[i] = "";  // All pointers point initially to empty string
    91   NParam = ParseInput(Command, Param);
    92 
    93   // Adress board
    94   if (Match(Param[0], "board")) {
     119  // Parse command into tokens
     120  Parameter.clear();
     121  char *Start;
     122  while(true) {
     123    while (isspace(*Command)) Command++; // Ignore initial white spaces
     124    if(*Command=='\0') break;
     125    if (*Command == '\"') {
     126          Start = ++Command;
     127      while(*Command!='\"' && *Command!='\0') Command++;
     128    }
     129    else {
     130          Start = Command;
     131      while(!isspace(*Command) && *Command!='\0') Command++;
     132    }
     133    if(*Command != '\0') *Command++ = '\0';
     134        Parameter.push_back(Start);
     135  }
     136
     137  // Search for command in command list
     138  for(unsigned int CmdNumber=0; CmdNumber<sizeof(CommandList)/sizeof(CL_Struct); CmdNumber++) {
     139    if (Match(Parameter[0], CommandList[CmdNumber].Name)) {
     140      if(Parameter.size()-1 < CommandList[CmdNumber].MinNumParameter) {
     141                PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters);
     142                return;
     143          }
     144
     145          // Jump to command function
     146          (this->*CommandList[CmdNumber].CommandPointer)();
     147          return; 
     148    }
     149  }
     150  PrintMessage("Unknown command '%s'\n", Parameter[0].c_str());
     151}
     152
     153
     154// Adress board
     155void ProcessIO::cmd_board() {
    95156   
    96     if (!NumHVBoards) return;
    97 
    98     // Print list of boards
    99     if (NParam == 1) {
    100       for (int i=0; i<NumHVBoards; i++) {
    101         PrintMessage("Board %d: %s\n", fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName);
    102       }
     157  // Print list of boards
     158  if (Parameter.size() == 1) {
     159    for (int i=0; i<NumHVBoards; i++) {
     160      PrintMessage("Board %d: %s\n", fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName);
     161    }
     162    return;
     163  }
     164
     165  //Select board(s)
     166  if (Match(Parameter[1].c_str(), "all")) {
     167    FirstBoard = 0;
     168    LastBoard = NumHVBoards-1;
     169  }
     170  else if (Parameter.size()==2 && atoi(Parameter[1].c_str())>=0 && atoi(Parameter[1].c_str())<NumHVBoards) {
     171    FirstBoard = atoi(Parameter[1].c_str());
     172    LastBoard = FirstBoard;
     173  }
     174  else if (Parameter.size()==3 && atoi(Parameter[1].c_str())>=0 && atoi(Parameter[1].c_str())<NumHVBoards &&
     175         atoi(Parameter[2].c_str())>0 && atoi(Parameter[2].c_str())<NumHVBoards) {
     176    FirstBoard = atoi(Parameter[1].c_str());
     177    LastBoard = atoi(Parameter[2].c_str());
     178  }
     179  else PrintMessage("Cannot address board(s), out of range.\n");
     180}
     181
     182// Adress chains
     183void ProcessIO::cmd_chain() {
     184   
     185  if (!NumHVBoards) return;
     186
     187  if (Match(Parameter[1],"all")) {
     188    FirstChain = 0;
     189    LastChain = 3;
     190  }
     191  else if (Parameter.size()==2 && atoi(Parameter[1].c_str())>=0 && atoi(Parameter[1].c_str())<NUM_CHAINS) {
     192    FirstChain = atoi(Parameter[1].c_str());
     193    LastChain = FirstChain;
     194  }
     195  else if (Parameter.size()==3 && atoi(Parameter[1].c_str())>=0 && atoi(Parameter[1].c_str())<NUM_CHAINS &&
     196         atoi(Parameter[2].c_str())>0 && atoi(Parameter[2].c_str())<NUM_CHAINS) {
     197    FirstChain = atoi(Parameter[1].c_str());
     198    LastChain = atoi(Parameter[2].c_str());
     199  }
     200  else PrintMessage("Cannot address chain(s), out of range.\n");
     201}
     202
     203// Print configuration
     204void ProcessIO::cmd_config() {
     205
     206  PrintMessage( " Pixel map table:   %s\n"
     207                " %d USB devices:\n", fPixMapTable, NumHVBoards); 
     208
     209  for (int i=0; i<NumHVBoards; i++) PrintMessage(" Board %d: %s\n", i, fHVBoard[i]->BoardName);
     210
     211  PrintMessage( " TimeOut:           %.2f s\n"
     212                " StatusRefreshRate: %.2f Hz\n"
     213                " DACMin value:      %d\n"
     214                " DACMax value:      %d\n"
     215                " HVCalibOffset :    %f\n"
     216                " HVCalibSlope :     %f\n"
     217                " HVMaxDiff :        %u\n",
     218                fTimeOut, fStatusRefreshRate, DACMin,
     219                DACMax, fHVCalibOffset, fHVCalibSlope, fHVMaxDiff);
     220}
     221
     222// Print help
     223void ProcessIO::cmd_help() {
     224
     225  char Buffer[MAX_COM_SIZE];
     226  for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
     227    snprintf(Buffer, sizeof(Buffer), "%s %s", CommandList[i].Name, CommandList[i].Parameters);
     228    PrintMessage("%-28s%s\n", Buffer, CommandList[i].Help);
     229  }
     230 
     231  PrintMessage(".<command>                  Execute shell command\n\n"
     232        "Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n");
     233}
     234
     235// Set new bias voltage
     236void ProcessIO::cmd_hv() {
     237
     238  int hvoltage, DACValue, Errors=0, Board=-1, Chain=-1, Channel=-1;
     239  double hvoltageV;
     240  bool SetDac;
     241
     242  // If array of channels, evaluate here (used by DIM)
     243  if (Parameter.size()>3 && pm->Pixel_to_HVboard(Parameter[3]) != 999999999) {
     244        for (unsigned int n=1; n<Parameter.size()-1; n+=2) {
     245          if (pm->Pixel_to_HVboard(Parameter[n]) != 999999999) {
     246        Board = pm->Pixel_to_HVboard(Parameter[n]);
     247        Chain = pm->Pixel_to_HVchain(Parameter[n]);
     248        Channel = pm->Pixel_to_HVchannel(Parameter[n]);
     249                hvoltageV = atof(Parameter[n+1].c_str());
     250                if (isdigit(Parameter[n+1][0])==0) fHVBoard[Board]->HVV[Chain][Channel] += hvoltageV;
     251            else fHVBoard[Board]->HVV[Chain][Channel] = hvoltageV;
     252            DACValue = calib->HVToDAC(fHVBoard[Board]->HVV[Chain][Channel], Board, Chain, Channel);
     253
     254          }
     255        }       
     256    return;
     257  }
     258
     259  // Evaluate voltage parameter
     260  if (Parameter.size()==4 && Match(Parameter[3], "dac")) {
     261    SetDac = true;
     262    if (!ConvertToInt(Parameter[2], &hvoltage)) {
     263      PrintMessage("Error: Wrong number format for DAC voltage setting\n");
    103264      return;
    104     }
    105 
    106     //Select board(s)
    107     if (Match(Param[1],"all")) {
    108       FirstBoard = 0;
    109       LastBoard = NumHVBoards-1;
    110265    }
    111     else if (NParam==2 && atoi(Param[1])>=0 && atoi(Param[1])<NumHVBoards) {
    112       FirstBoard = atoi(Param[1]);
    113       LastBoard = FirstBoard;
    114     }
    115     else if (NParam==3 && atoi(Param[1])>=0 && atoi(Param[1])<NumHVBoards &&
    116            atoi(Param[2])>0 && atoi(Param[2])<NumHVBoards) {
    117       FirstBoard = atoi(Param[1]);
    118       LastBoard = atoi(Param[2]);
    119     }
    120     else PrintMessage("Cannot address board(s), out of range.\n");
    121 
     266  }
     267  else {
     268    SetDac = false;
     269    if (!ConvertToDouble(Parameter[2], &hvoltageV)) {
     270      PrintMessage("Error: Wrong number format for voltage setting\n");
     271      return;
     272    }
     273  }
     274
     275  // Evaluate pixel or channel parameter   
     276  if(pm->Pixel_to_HVboard(Parameter[1]) != 999999999) {
     277    Board = pm->Pixel_to_HVboard(Parameter[1]);
     278    Chain = pm->Pixel_to_HVchain(Parameter[1]);
     279    Channel = pm->Pixel_to_HVchannel(Parameter[1]);
     280  }
     281  else if (!Match(Parameter[1], "all") && !ConvertToInt(Parameter[1], &Channel)) {
     282    PrintMessage("Error: Wrong channel identification\n");
    122283    return;
    123   }
    124 
    125 
    126   // Adress chains
    127   else if (Match(Param[0], "chain")) {
     284  }
     285
     286  for (int i=FirstBoard; i<=LastBoard; i++) {
     287    if (i!=Board && Board!=-1) continue;
     288  for (int j=FirstChain; j<=LastChain; j++) {
     289      if (j!=Chain && Chain!=-1) continue;
     290  for (int k=0; k<NUM_CHANNELS; k++) {
     291        if (k!=Channel && Channel!=-1) continue;
     292
     293        // Voltage change ignored if DAC value is zero
     294        if (isdigit(Parameter[2][0])==0 && fHVBoard[i]->HV[j][k] == 0) continue;
     295
     296        // Determine new DAC values
     297        if (!SetDac){
     298          if (isdigit(Parameter[2][0])==0) fHVBoard[i]->HVV[j][k] += hvoltageV; // voltage change
     299          else fHVBoard[i]->HVV[j][k] = hvoltageV;
     300          DACValue = calib->HVToDAC(fHVBoard[i]->HVV[j][k], i, j, k);
     301        }
     302        else {
     303          if (isdigit(Parameter[2][0])==0) DACValue = fHVBoard[i]->HV[j][k] + hvoltage; // voltage change
     304          else DACValue = hvoltage;
     305        }
     306
     307        // Set new voltage (if DAC value, update calibrated value)
     308        if (!RampVoltage(DACValue, i, j, k)) Errors++;
     309        if (SetDac) fHVBoard[i]->HVV[j][k] = calib->DACToHV(fHVBoard[i]->HV[j][k], i, j, k);
     310  } // Channels
     311  } // Chains
     312
     313  // Update DIM service
     314  fHVBoard[i]->BiasVolt->updateService();
     315  } // Boards
     316
     317  if (Errors > 0) PrintMessage("Errors on %d channel(s) occurred\n", Errors);
     318}
     319
     320// Load bias settings from file
     321void ProcessIO::cmd_load() {
     322
     323  char Buffer[MAX_COM_SIZE];
     324  int NBoards = 0, Errors = 0, Chain, Channel;
     325  unsigned int DACValue;
     326  FILE *File;
     327
     328  if ((File=fopen(Parameter[1].c_str(), "r")) == NULL) {
     329    PrintMessage("Error: Could not open file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
     330    return;
     331  }
     332
     333  while (fgets(Buffer, sizeof(Buffer), File) != NULL) {
     334    for (int Board=0; Board<NumHVBoards; Board++) {
     335          if (Match(fHVBoard[Board]->BoardName, Buffer)) {
     336                PrintMessage("Found bias settings for board %d (%s)\n\r",fHVBoard[Board]->GetBoardNumber(), fHVBoard[Board]->BoardName);
     337
     338                Chain = 0;  Channel = 0;
     339                while (fscanf(File, "%u", &DACValue)==1 && Chain<NUM_CHAINS) {
     340                  if (!RampVoltage(DACValue, Board, Chain, Channel)) {
     341                Errors++;
     342                PrintMessage("Error: Could not ramp chain %d, channel %d\n", Chain, Channel);
     343                  }
     344                  else {
     345                PrintMessage("Ramped chain %d, channel %d to %u (%.2f V)                         \r",
     346                   Chain,Channel,DACValue,calib->DACToHV(DACValue,Board,Chain,Channel));
     347                  }
     348                  fHVBoard[Board]->HVV[Chain][Channel] = calib->DACToHV(fHVBoard[Board]->HV[Chain][Channel], Board, Chain, Channel);
     349
     350                  if(++Channel == NUM_CHANNELS) {
     351                Chain++;
     352                Channel = 0;
     353                  }
     354                }
     355
     356        // Update DIM service
     357                fHVBoard[Board]->BiasVolt->updateService();
     358
     359                if (ferror(File) != 0) {
     360                  PrintMessage("Error reading DAC value from file, terminating. (%s)\n",strerror(errno));
     361                  return;
     362                }
     363                else PrintMessage("\nFinished updating board\n");
     364        NBoards++;
     365          }
     366    } // Loop over boards
     367  } // while()
     368           
     369  if (NBoards != NumHVBoards) {
     370    PrintMessage("Warning: Could not load bias settings for all connected HV boards\n");
     371  }
     372  else if (Errors == 0) PrintMessage("Success: Read bias settings for all connected HV boards\n");
     373  if (Errors != 0) PrintMessage("Warning: Errors on %d channel(s) occurred\n", Errors);
     374
     375  if (fclose(File) != 0) PrintMessage("Error: Could not close file '%s'\n",Parameter[1].c_str());
     376}
     377           
     378// Set status refresh rate
     379void ProcessIO::cmd_rate() {
     380
     381  double Rate;
     382
     383  if (!ConvertToDouble(Parameter[1], &Rate)) {
     384     PrintMessage("Error: Wrong number format\n");
     385     return;   
     386  }
     387
     388  // Check limits
     389  if (Rate<MIN_RATE || Rate>MAX_RATE) {
     390    PrintMessage("Refresh rate out of range (min: %.2f Hz, max: %.2f Hz)\n", MIN_RATE, MAX_RATE);
     391    return;
     392  }
     393
     394  fStatusRefreshRate = Rate;
     395  PrintMessage("Refresh rate set to %.2f Hz\n", fStatusRefreshRate);
     396}
     397 
     398// Reset
     399void ProcessIO::cmd_reset() {
     400
     401  for (int i=FirstBoard; i<=LastBoard; i++) ResetBoard(i);
     402}
     403
     404// Save bias settings of all boards
     405void ProcessIO::cmd_save() {
     406
     407  FILE *File;
     408  time_t time_now_secs;
     409  struct tm *Time;
     410
     411  time(&time_now_secs);
     412  Time = localtime(&time_now_secs);
     413
     414  if ((File = fopen(Parameter[1].c_str(), "w")) == NULL) {
     415    PrintMessage("Error: Could not open file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
     416    return;
     417  }
     418
     419  fprintf(File,"********** Bias settings, %04d %02d %02d, %02d:%02d:%02d **********\n\n",
     420          1900 + Time->tm_year, 1 + Time->tm_mon,
     421          Time->tm_mday, Time->tm_hour, Time->tm_min, Time->tm_sec);
     422
     423  for (int i=0; i<NumHVBoards; i++) {
     424    fprintf(File, "%s\n\n", fHVBoard[i]->BoardName);
     425
     426    for (int j=0; j<NUM_CHAINS; j++) {
     427  for (int k=0; k<NUM_CHANNELS; k++) fprintf(File,"%5d ",fHVBoard[i]->HV[j][k]);
     428  fprintf(File, "\n");
     429    }
     430    fprintf(File, "\n");
     431  }
     432
     433  if (fclose(File) != 0) {
     434    PrintMessage("Error: Could not close file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno)); 
     435  }
     436}
     437
     438// Start monitoring
     439void ProcessIO::cmd_start() {
     440
     441  state = active;
     442  pthread_kill(HVMonitor, SIGUSR1);
     443  PrintMessage(All, "Status monitoring activated\n");
     444}
     445
     446// Print status
     447void ProcessIO::cmd_status() {
     448
     449  PrintMessage("\n Status monitor: %s\n", state_str[state]);
     450  PrintMessage(" Status refresh rate [Hz]: %.2f\n", fStatusRefreshRate);
     451  PrintMessage(" Total number of boards: %d\n", NumHVBoards);
     452  PrintMessage(" Active boards: %d\n\n", LastBoard - FirstBoard + 1);
     453
     454  for (int i=FirstBoard; i<=LastBoard; i++) {
     455    PrintMessage(" BOARD %d (%s)   Wrap counter: %s (%d)  Manual reset: %s\n    Time-out: %.2f s  Error count: %d\n\n",
     456  fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName,
     457  fHVBoard[i]->WrapOK ? "ok":"error",
     458  fHVBoard[i]->LastWrapCount,
     459  fHVBoard[i]->ResetButton ? "yes" : "no",
     460  fHVBoard[i]->fTimeOut,
     461  fHVBoard[i]->ErrorCount);
     462
     463    for (int j=FirstChain; j<=LastChain; j++) {
     464          PrintMessage("  CHAIN %d     Over-current: %s\n", j, fHVBoard[i]->Overcurrent[j] ? "yes" : "no");
     465          for (int k=0;k<4;k++) {
     466        PrintMessage("\r");
     467            for (int l=0;l<8;l++) {
     468                  if(Parameter.size() == 2) PrintMessage("%5d ",fHVBoard[i]->HV[j][k*8+l]);
     469          else PrintMessage("%#5.2f ",fHVBoard[i]->HVV[j][k*8+l]);
     470        }
     471            PrintMessage("\n");
     472          }
     473    }
     474  }
     475}
     476
     477// Stop monitoring
     478void ProcessIO::cmd_stop() {
     479
     480  state = stopped;
     481  pthread_kill(HVMonitor, SIGUSR1);
     482  PrintMessage(All, "Status monitor stopped\n");
     483}
     484
     485// Set timeout to return from read
     486void ProcessIO::cmd_timeout() {
     487
     488  double Timeout;
     489
     490  if (!ConvertToDouble(Parameter[1], &Timeout)) {
     491     PrintMessage("Error: Wrong number format\n");
     492     return;   
     493  }
     494
     495  for (int i=0; i<NumHVBoards; i++) fHVBoard[i]->SetTimeOut(Timeout);
     496  if (NumHVBoards > 0) PrintMessage("Timeout set to %.2f s for all boards\n", Timeout);
     497}
    128498   
    129     if (!NumHVBoards) return;
    130    
    131     if (Match(Param[1],"all")) {
    132       FirstChain = 0;
    133       LastChain = 3;
    134     }
    135     else if (NParam==2 && atoi(Param[1])>=0 && atoi(Param[1])<NUM_CHAINS) {
    136       FirstChain = atoi(Param[1]);
    137       LastChain = FirstChain;
    138     }
    139     else if (NParam==3 && atoi(Param[1])>=0 && atoi(Param[1])<NUM_CHAINS &&
    140            atoi(Param[2])>0 && atoi(Param[2])<NUM_CHAINS) {
    141       FirstChain = atoi(Param[1]);
    142       LastChain = atoi(Param[2]);
    143     }
    144     else PrintMessage("Cannot address chain(s), out of range.\n");
    145        
    146     return;
    147   }
    148 
    149 
    150   // Print HV utility configuration
    151   else if (Match(Param[0], "config")) {
    152     PrintMessage( " Pixel map table:   %s\n"
    153                   " %d USB devices:\n", fPixMapTable,
    154                   NumHVBoards); 
    155     for (int i=0; i<NumHVBoards; i++) {
    156       PrintMessage(" Board %d: %s\n", i, fHVBoard[i]->BoardName);
    157     }
    158     PrintMessage( " TimeOut:           %.2f s\n"
    159                   " StatusRefreshRate: %.2f Hz\n"
    160                   " DACMin value:      %d\n"
    161                   " DACMax value:      %d\n"
    162                   " HVCalibOffset :    %f\n"
    163                   " HVCalibSlope :     %f\n"
    164                   " HVMaxDiff :        %u\n", fTimeOut,
    165                   fStatusRefreshRate, DACMin,
    166                   DACMax, fHVCalibOffset, fHVCalibSlope,
    167                   fHVMaxDiff);
    168 
    169     return;
    170   }
    171 
    172 
    173   // Print help
    174   if (Match(Param[0], "help")) {
    175     PrintMessage(" board <i>|<i> <j>|<all>           Address board i, boards i-j or all boards or list boards\n"
    176         " chain <i>|<i> <j>|<all>           Address chain i, chains i-j or all chains\n"
    177         " hv <PXL id>|<ch>|<all> <v>        Set/change bias of pixel, ch. or all ch. of active chain(s)/board(s)\n"
    178                 " status [dac]                      Show status information (DAC values if requested)\n"
    179                 " config                            Print configuration\n"
    180                 " load <file>                       Load and set bias settings from <file>\n"
    181                 " save <file>                       Save current bias settings to [file]\n"
    182                 " exit                              Exit program\n"
    183                 " rate <rate>                       Set status refresh rate to <rate> [Hz]\n"
    184                 " timeout <time>                    Set timeout to return from read to <time> [s]\n"
    185                 " reset                             Reset active bias board\n"
    186                 " start                             Start bias status monitor\n"
    187                 " stop                              Stop bias status monitor - not recommended!\n"
    188                 " uptime                            Get program uptime [h:m:s]\n"
    189                 " help                              Print help\n"
    190                 " .<cmd>                             Execute shell command <cmd>");
    191 
    192     return;
    193   }
    194 
    195 
    196   // Set new bias voltage (if no boards available, simply returns OK)
    197   // First reponse to socket should be 'OK' if no error occurred.
    198   if (Match(Param[0], "hv")) {
    199 
    200     int hvoltage, DACValue, Errors=0, Board=-1, Chain=-1, Channel=-1;
    201     double hvoltageV;
    202     bool SetDac;
    203    
    204     // Need two or parameters
    205     if (NParam<3 || NParam>4) {
    206       PrintMessage("Usage: hv <channel>|<all> <voltage> [dac]\n");
    207       return;
    208     }
    209 
    210     // Evaluate voltage parameter
    211     if (NParam==4 && Match(Param[3], "dac")) {
    212       SetDac = true;
    213       if (!ConvertToInt(Param[2], &hvoltage)) {
    214         PrintMessage("Error: Wrong number format for DAC voltage setting\n");
    215         return;
    216       }
    217     }
    218     else {
    219       SetDac = false;
    220       if (!ConvertToDouble(Param[2], &hvoltageV)) {
    221         PrintMessage("Error: Wrong number format for voltage setting\n");
    222         return;
    223       }
    224     }
    225 
    226     // Evaluate pixel or channel parameter   
    227     if(pm->Pixel_to_HVboard(Param[1]) != 999999999) {
    228       Board = pm->Pixel_to_HVboard(Param[1]);
    229       Chain = pm->Pixel_to_HVchain(Param[1]);
    230       Channel = pm->Pixel_to_HVchannel(Param[1]);
    231     }
    232     else if (!Match(Param[1], "all") && !ConvertToInt(Param[1], &Channel)) {
    233       PrintMessage("Error: Wrong channel identification\n");
    234       return;
    235     }
    236 
    237     for (int i=FirstBoard; i<=LastBoard; i++) {
    238       if (i!=Board && Board!=-1) continue;
    239     for (int j=FirstChain; j<=LastChain; j++) {
    240         if (j!=Chain && Chain!=-1) continue;
    241         for (int k=0; k<NUM_CHANNELS; k++) {
    242           if (k!=Channel && Channel!=-1) continue;
    243 
    244           // Voltage change ignored if DAC value is zero
    245           if (isdigit(*Param[2])==0 && fHVBoard[i]->HV[j][k] == 0) continue;
    246 
    247           // Determine new DAC values
    248           if (!SetDac){
    249                 if (isdigit(*Param[2])==0) fHVBoard[i]->HVV[j][k] += hvoltageV; // voltage change
    250             else fHVBoard[i]->HVV[j][k] = hvoltageV;
    251             DACValue = calib->HVToDAC(fHVBoard[i]->HVV[j][k], i, j, k);
    252           }
    253           else {
    254             if (isdigit(*Param[2])==0) DACValue = fHVBoard[i]->HV[j][k] + hvoltage; // voltage change
    255             else DACValue = hvoltage;
    256           }
    257          
    258           // Set new voltage (if DAC value, update calibrated value)
    259           if (!RampVoltage(DACValue, i, j, k)) Errors++;
    260           if (SetDac) fHVBoard[i]->HVV[j][k] = calib->DACToHV(fHVBoard[i]->HV[j][k], i, j, k);
    261 
    262       // Update DIM services
    263       //fHVBoard[i]->BiasVolt[j][k]->updateService();
    264           UpdateDelay = 1.0;
    265         } // Channels   
    266     } // Chains
    267     } // Boards
    268 
    269     if (Errors > 0) PrintMessage("Errors on %d channel(s) occurred\n", Errors);
    270    
    271     return;
    272   }
    273 
    274 
    275   // Load HV settings from file
    276   else if (Match(Param[0], "load")) {
    277 
    278     char Buffer[MAX_COM_SIZE];
    279     int NBoards = 0, Errors = 0, Chain, Channel;
    280     unsigned int DACValue;
    281     FILE *File;
    282    
    283     if (NParam != 2) {
    284       PrintMessage("Usage: load <file>\n");
    285       return;
    286     }
    287    
    288     if ((File=fopen(Param[1], "r")) == NULL) {
    289       PrintMessage("Error: Could not open file '%s' (%s)\n", Param[1], strerror(errno));
    290       return;
    291     }
    292        
    293     while (fgets(Buffer, sizeof(Buffer), File) != NULL) {
    294       for (int Board=0; Board<NumHVBoards; Board++) {
    295         if (Match(fHVBoard[Board]->BoardName, Buffer)) {
    296           PrintMessage("Found bias settings for board %d (%s)\n\r",fHVBoard[Board]->GetBoardNumber(), fHVBoard[Board]->BoardName);
    297 
    298           Chain = 0;  Channel = 0;
    299           while (fscanf(File, "%u", &DACValue)==1 && Chain<NUM_CHAINS) {
    300             if (!RampVoltage(DACValue, Board, Chain, Channel)) {
    301               Errors++;
    302               PrintMessage("Error: Could not ramp chain %d, channel %d\n", Chain, Channel);
    303             }
    304             else {
    305               PrintMessage("Ramped chain %d, channel %d to %u (%.2f V)                         \r",
    306                  Chain,Channel,DACValue,calib->DACToHV(DACValue,Board,Chain,Channel));
    307             }
    308             fHVBoard[Board]->HVV[Chain][Channel] = calib->DACToHV(fHVBoard[Board]->HV[Chain][Channel], Board, Chain, Channel);
    309 
    310             // Update DIM services
    311             //fHVBoard[Board]->BiasVolt[Chain][Channel]->updateService();
    312                 UpdateDelay = 1.0;
    313             if(++Channel == NUM_CHANNELS) {
    314               Chain++;
    315               Channel = 0;
    316             }
    317           }
    318           if (ferror(File) != 0) {
    319             PrintMessage("Error reading DAC value from file, terminating. (%s)\n",strerror(errno));
    320             return;
    321           }
    322           else PrintMessage("\nFinished updating board\n");
    323           NBoards++;
    324         }
    325       } // Loop over boards
    326     } // while()
    327            
    328     if (NBoards != NumHVBoards) {
    329       PrintMessage("Warning: Could not load bias settings for all connected HV boards\n");
    330     }
    331     else if (Errors == 0) PrintMessage("Success: Read bias settings for all connected HV boards\n");
    332     if (Errors != 0) PrintMessage("Warning: Errors on %d channel(s) occurred\n", Errors);
    333    
    334     if (fclose(File) != 0) PrintMessage("Error: Could not close file '%s'\n",Param[1]);
    335 
    336     return;   
    337   }
    338            
    339 
    340   // Set status refresh rate
    341   if (Match(Param[0], "rate")) {
    342 
    343     double Rate;
    344    
    345     if (NParam != 2) {
    346       PrintMessage("Usage: rate <Hz>\n");
    347       return;
    348     }
    349    
    350     if (!ConvertToDouble(Param[1], &Rate)) {
    351        PrintMessage("Error: Wrong number format\n");
    352        return;   
    353     }
    354 
    355     // Check limits
    356     if (Rate<MIN_RATE || Rate>MAX_RATE) {
    357       PrintMessage("Refresh rate out of range (min: %.2f Hz, max: %.2f Hz)\n", MIN_RATE, MAX_RATE);
    358       return;
    359     }
    360 
    361     fStatusRefreshRate = Rate;
    362     PrintMessage("Refresh rate set to %.2f Hz\n", fStatusRefreshRate);
    363 
    364     return;
    365   }
    366  
    367   // Reset
    368   if (Match(Param[0], "reset")) {
    369 
    370     if (!NumHVBoards) return;
    371     for (int i=FirstBoard; i<=LastBoard; i++) ResetBoard(i);
    372     return;
    373   }
    374 
    375 
    376   // Save HV settings of all boards
    377   else if (Match(Param[0], "save")) {
    378 
    379     FILE *File;
    380     time_t time_now_secs;
    381     struct tm *Time;
    382 
    383     if (NParam != 2) {
    384       PrintMessage("Usage: save <Filename>\n");
    385       return;
    386     }
    387  
    388     time(&time_now_secs);
    389     Time = localtime(&time_now_secs);
    390 
    391     if ((File = fopen(Param[1], "w")) == NULL) {
    392       PrintMessage("Error: Could not open file '%s' (%s)\n", Param[1], strerror(errno));
    393       return;
    394     }
    395  
    396     fprintf(File,"********** Bias settings, %04d %02d %02d, %02d:%02d:%02d **********\n\n",
    397             1900 + Time->tm_year, 1 + Time->tm_mon,
    398             Time->tm_mday, Time->tm_hour, Time->tm_min, Time->tm_sec);
    399  
    400     for (int i=0; i<NumHVBoards; i++) {
    401       fprintf(File, "%s\n\n", fHVBoard[i]->BoardName);
    402 
    403       for (int j=0; j<NUM_CHAINS; j++) {
    404         for (int k=0; k<NUM_CHANNELS; k++) fprintf(File,"%5d ",fHVBoard[i]->HV[j][k]);
    405         fprintf(File, "\n");
    406       }
    407       fprintf(File, "\n");
    408     }
    409 
    410     if (fclose(File) != 0) {
    411       PrintMessage("Error: Could not close file '%s' (%s)\n", Param[1], strerror(errno)); 
    412     }
    413 
    414     return;
    415   }
    416 
    417 
    418   // Start monitoring
    419   else if (Match(Param[0], "start")) {
    420 
    421     state = active;
    422     pthread_kill(HVMonitor, SIGUSR1);
    423     PrintMessage(All, "Status monitoring activated\n");
    424     return; 
    425   }
    426 
    427  
    428   // Print status
    429   else if (Match(Param[0], "status")) {
    430    
    431     PrintMessage("\n Status monitor: %s\n", state_str[state]);
    432     PrintMessage(" Status refresh rate [Hz]: %.2f\n", fStatusRefreshRate);
    433     PrintMessage(" Total number of boards: %d\n", NumHVBoards);
    434     PrintMessage(" Active boards: %d\n\n", LastBoard - FirstBoard + 1);
    435 
    436     for (int i=FirstBoard; i<=LastBoard; i++) {
    437       PrintMessage(" BOARD %d (%s)   Wrap counter: %s (%d)  Manual reset: %s\n    Time-out: %.2f s  Error count: %d\n\n",
    438         fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName,
    439         fHVBoard[i]->WrapOK ? "ok":"error",
    440         fHVBoard[i]->LastWrapCount,
    441         fHVBoard[i]->ResetButton ? "yes" : "no",
    442         fHVBoard[i]->fTimeOut,
    443         fHVBoard[i]->ErrorCount);
    444 
    445       for (int j=FirstChain; j<=LastChain; j++) {
    446         PrintMessage("  CHAIN %d     Over-current: %s\n", j, fHVBoard[i]->Overcurrent[j] ? "yes" : "no");
    447         for (int k=0;k<4;k++) {
    448           PrintMessage("\r");
    449           for (int l=0;l<8;l++) {
    450             if(NParam == 2) PrintMessage("%5d ",fHVBoard[i]->HV[j][k*8+l]);
    451             else PrintMessage("%#5.2f ",fHVBoard[i]->HVV[j][k*8+l]);
    452           }
    453           PrintMessage("\n");
    454         }
    455       }
    456     }
    457 
    458     return;
    459   }
    460 
    461 
    462   // Stop monitoring
    463   else if (Match(Param[0], "stop")) {
    464 
    465     state = stopped;
    466     pthread_kill(HVMonitor, SIGUSR1);
    467     PrintMessage(All, "Status monitor stopped\n");
    468 
    469     return;
    470   }
    471 
    472 
    473   // Set timeout to return from read
    474   if (Match(Param[0], "timeout")) {
    475 
    476     double Timeout;
    477    
    478     if (!NumHVBoards) return;
    479 
    480     if (NParam != 2) {
    481       PrintMessage("Usage: timeout <secs>\n");
    482       return;
    483     }
    484    
    485     if (!ConvertToDouble(Param[1], &Timeout)) {
    486        PrintMessage("Error: Wrong number format\n");
    487        return;   
    488     }
    489 
    490     for (int i=0; i<NumHVBoards; i++) fHVBoard[i]->SetTimeOut(Timeout);
    491     PrintMessage("Timeout set to %.2f s for all boards\n", Timeout);
    492    
    493     return;
    494   }
    495 
    496    
    497   // Print uptime
    498   if (Match(Param[0], "uptime")) {
    499     time_t ActualT;
    500     time (&ActualT);
    501 
    502     PrintMessage("%d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60);
    503 
    504     return;
    505   }
    506 
    507 
    508   // Exit program
    509   else if(Match(Param[0], "exit")) {
    510 
    511     Exit = true;
    512     pthread_kill(HVMonitor, SIGUSR1);
    513     return;
    514   }
    515  
    516   PrintMessage("Unknown command '%s'\n", Param[0]);
    517  
    518   return;
    519 }
    520 
     499// Print uptime
     500void ProcessIO::cmd_uptime() {
     501
     502  time_t ActualT;
     503  time (&ActualT);
     504  PrintMessage("%d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60);
     505}
     506
     507// Exit program
     508void ProcessIO::cmd_exit() {
     509
     510  Exit = true;
     511  pthread_kill(HVMonitor, SIGUSR1);
     512}
     513 
    521514
    522515// Print message to selected target
    523516void ProcessIO::PrintMessage(MsgTarget Target, const char *Format, ...) {
     517
    524518  va_list ArgumentPointer;
    525519  va_start(ArgumentPointer, Format);
     
    530524// Print message to log file, and screen or socket (depending on command origin)
    531525void ProcessIO::PrintMessage(const char *Format, ...) {
     526
    532527  va_list ArgumentPointer;
    533528  va_start(ArgumentPointer, Format);
     
    567562  }
    568563}
    569 
    570564
    571565// Ramp to new voltage with maximum step size given in fHVMaxDiff
     
    626620    }
    627621  }
    628 
    629   if (UpdateDelay >= 0) {
    630         UpdateDelay -= 1.0/(NumHVBoards*fStatusRefreshRate);
    631         if (UpdateDelay <= 0) for (int i=0; i<NumHVBoards; i++) {
    632           fHVBoard[i]->BiasVolt->updateService();
    633         }
    634   }
    635622}
    636623
     
    661648
    662649
    663 // Parse command line for white space and double-quote separated tokens
    664 int ProcessIO::ParseInput(char* Command, const char *Param[]) {
    665   int Count=0;
    666    
    667   while(Count<MAX_NUM_TOKEN) {
    668     while (isspace(*Command)) Command++; // Ignore initial white spaces
    669     if(*Command=='\0') break;
    670     if (*Command == '\"') {
    671       Param[Count] = ++Command;
    672       while(*Command!='\"' && *Command!='\0') Command++;
    673     }
    674     else {
    675       Param[Count] = Command;
    676       while(!isspace(*Command) && *Command!='\0') Command++;
    677     }
    678     if(*Command != '\0') *Command++ = '\0';
    679     Count++;
    680   }
    681   return Count;
    682 }
    683 
    684 //
    685 // Command handling (the only command is 'Bias/Command')
    686 //
     650// Command handling (mutex needed because of monitor thread)
    687651void ProcessIO::commandHandler() {
    688652
    689     pthread_mutex_lock(&control_mutex);
    690     CommandControl(getString());
    691     pthread_mutex_unlock(&control_mutex);
     653  pthread_mutex_lock(&control_mutex);
     654  if (getCommand() == Command) CommandControl(getCommand()->getString());
     655  pthread_mutex_unlock(&control_mutex);
    692656}
    693657
    694658// Check if two strings match (min 1 character must match)
    695 bool Match(const char *str, const char *cmd) {
    696   return strncasecmp(str,cmd,strlen(str)==0 ? 1:strlen(str)) ? false:true;
     659bool Match(string str, const char *cmd) {
     660  return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true;
    697661}
    698662
     
    700664// Convert string to double
    701665// Returns false if conversion did not stop on whitespace or EOL character
    702 bool ConvertToDouble(const char *String, double *Result) {
     666bool ConvertToDouble(string String, double *Result) {
    703667
    704668  char *EndPointer;
    705669 
    706   *Result = strtod(String, &EndPointer);
     670  *Result = strtod(String.c_str(), &EndPointer);
    707671  if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
    708672  return true;
     
    712676// Convert string to int
    713677// Returns false if conversion did not stop on whitespace or EOL character
    714 bool ConvertToInt(const char *String, int *Result) {
     678bool ConvertToInt(string String, int *Result) {
    715679
    716680  char *EndPointer;
    717681 
    718   *Result = (int) strtol(String, &EndPointer, 0);
     682  *Result = (int) strtol(String.c_str(), &EndPointer, 0);
    719683  if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
    720684  return true;
  • hvcontrol/src/ProcessIO.h

    r210 r220  
    1818#define NUM_CHAINS 4
    1919#define NUM_CHANNELS 32
    20 #define BUFFER_LENGTH 256
    2120
    2221#define MIN_TIMEOUT 0.01
     
    2625#define MAX_RATE 50.0
    2726
    28 #define MAX_NUM_TOKEN 10
    29 
    3027enum MsgTarget {Console=1, Log=2, All=7};
    3128typedef enum stateenum {active, stopped, na} state_enum;
    3229
    33 class ProcessIO: public DimCommand, public EvidenceServer {
     30class ProcessIO: public EvidenceServer {
    3431
    35   time_t StartTime;
    36   PixelMap *pm;
    37   float UpdateDelay;
     32        time_t StartTime;
     33        PixelMap *pm;
     34        DimCommand *Command;
    3835
    39   void commandHandler();
     36        void commandHandler();
    4037
    4138 public:
     39        HVCalib     *calib;
     40        HVBoard **fHVBoard;
    4241
    43   HVCalib     *calib;
    44   HVBoard **fHVBoard;
     42        pthread_mutex_t control_mutex;
     43
     44        char Prompt[MAX_COM_SIZE];
     45        std::vector<std::string> Parameter;
     46
     47        // Configuration data
     48        char *fPixMapTable;
     49        float fTimeOut;
     50        float fStatusRefreshRate;
     51        int DACMin;
     52        int DACMax;
     53        float fHVCalibOffset;
     54        float fHVCalibSlope;
     55        unsigned int fHVMaxDiff;
     56
     57        // Status variables 
     58        pthread_t HVMonitor;       // exit function sends signal to these threads
     59
     60        int NumHVBoards;
     61        int FirstBoard;
     62        int LastBoard;
     63        int FirstChain;
     64        int LastChain;
    4565 
    46   pthread_mutex_t control_mutex;
     66        state_enum   state;
     67        bool Exit;
    4768
    48   char Prompt[MAX_COM_SIZE];
    49   const char *Param[MAX_NUM_TOKEN]; // For parser
    50   int NParam;
     69        // Methods
     70        ProcessIO();
     71        ~ProcessIO();
    5172
    52   // Configuration data
    53   char *fPixMapTable;
    54   float fTimeOut;
    55   float fStatusRefreshRate;
    56   int DACMin;
    57   int DACMax;
    58   float fHVCalibOffset;
    59   float fHVCalibSlope;
    60   unsigned int fHVMaxDiff;
    61 
    62   // Status variables 
    63   pthread_t HVMonitor;       // exit function sends signal to these threads
    64  
    65   int NumHVBoards;
    66   int FirstBoard;
    67   int LastBoard;
    68   int FirstChain;
    69   int LastChain;
    70  
    71   state_enum   state;
    72   bool Exit;
    73    
    74   // Methods
    75   ProcessIO();
    76   ~ProcessIO();
    77 
    78   void PrintMessage(MsgTarget, const char *, ...);
    79   void PrintMessage(const char *, ...);
    80   void DoPrintMessage(const char *, va_list, MsgTarget);
    81   void CommandControl(char*);
    82   bool RampVoltage(unsigned int, int, int, int);
    83   void Monitor();
    84   void ResetBoard(int);
    85   void PrintBoardStatus(int);
    86   int ParseInput(char*, const char *Param[]);
     73        void PrintMessage(MsgTarget, const char *, ...);
     74        void PrintMessage(const char *, ...);
     75        void DoPrintMessage(const char *, va_list, MsgTarget);
     76        void CommandControl(char*);
     77        bool RampVoltage(unsigned int, int, int, int);
     78        void Monitor();
     79        void ResetBoard(int);
     80        void PrintBoardStatus(int);
     81       
     82        void cmd_board();       void cmd_hv();
     83        void cmd_status();      void cmd_config();
     84        void cmd_load();        void cmd_save();
     85        void cmd_exit();        void cmd_rate();
     86        void cmd_timeout();     void cmd_reset();
     87        void cmd_start();       void cmd_stop();
     88        void cmd_uptime();      void cmd_help();
     89        void cmd_chain();
    8790};
    8891
    89 bool Match(const char *, const char *);
    90 bool ConvertToDouble(const char *, double *);
    91 bool ConvertToInt(const char *, int *);
     92bool Match(std::string, const char *);
     93bool ConvertToDouble(std::string, double *);
     94bool ConvertToInt(std::string, int *);
    9295
    9396#endif
Note: See TracChangeset for help on using the changeset viewer.