Index: fact/BIASctrl/BIASctrl.cc
===================================================================
--- fact/BIASctrl/BIASctrl.cc	(revision 10049)
+++ fact/BIASctrl/BIASctrl.cc	(revision 10052)
@@ -72,6 +72,6 @@
  
   // Install signal handler and set signal SIGUSR1 to interrupt blocking system calls
-  signal(SIGUSR1, &DummyHandler);
-  siginterrupt (SIGUSR1, true);
+  //signal(SIGUSR1, &DummyHandler);
+  //siginterrupt (SIGUSR1, true);
 
   // Assure lock file is deleted in case of a program crash or call to exit()
Index: fact/BIASctrl/Crate.cc
===================================================================
--- fact/BIASctrl/Crate.cc	(revision 10049)
+++ fact/BIASctrl/Crate.cc	(revision 10052)
@@ -19,4 +19,5 @@
   // Initialize
   InitOK = false;
+  File = NULL;
   m = PIO;
   Name = new char [CrateName.size()+1];
@@ -50,4 +51,10 @@
     return;
   }
+  
+  // Generate FILE pointer
+  if ((File = fdopen(fDescriptor, "rb+")) == NULL) {
+    m->PrintMessage("Error: fdopen() failed on device %d/%s (%s)\n", CrateNumber, Name, strerror(errno));
+    return;
+  }
 
   // Get current serial port settings
@@ -77,6 +84,11 @@
 
   if(fDescriptor != -1) {
+    GlobalSet(0);
+
     SystemReset();
-    if (close(fDescriptor) == -1) m->PrintMessage("Error closing device %s (%s)\n", Name, strerror(errno));;
+    if (File == NULL) {
+	  if (close(fDescriptor) == -1) m->PrintMessage("Error closing device %s (%s)\n", Name, strerror(errno));
+	}
+    else if (fclose(File) != 0) m->PrintMessage("Error closing device %s\n", Name);
   }
 
@@ -90,16 +102,15 @@
 //
 // Returns: 0 error, 1 success, -1 time-out exceeded
-int Crate::Communicate(unsigned char* wbuf, int Bytes) {
-
-  int N, Ret = 0;
+vector<char> Crate::Communicate(unsigned char* wbuf, int Bytes) {
+
+  int N;
   fd_set SelectDescriptor;
   struct timeval WaitTime = {(long) m->fTimeOut, (long) ((m->fTimeOut-(long) m->fTimeOut)*1e6)};
-  
-  // === Lock file descriptor ===
-  if (lockf(fDescriptor, F_LOCK, 0) == -1) {
-	m->Message(m->ERROR, "Failed to lock file descriptor (%s)", strerror(errno));
-	return 0;
-  }
-
+  char Buffer[10000];
+  vector<char> Data;
+  
+  // === Lock device ===
+  flockfile(File);
+  
   // === Write data ===
   if ((N = write(fDescriptor, wbuf, Bytes)) < Bytes) {
@@ -110,42 +121,51 @@
   }
 
-  // === Try to read until time-out ===
-  FD_ZERO(&SelectDescriptor);   FD_SET(fDescriptor, &SelectDescriptor);
-  if (select(fDescriptor+1, &SelectDescriptor, NULL, NULL, &WaitTime)==-1) {
-    m->Message(m->ERROR, "Error with select() (%s)", strerror(errno));
+  // === Try to read back data with time-out ===
+  do {
+	FD_ZERO(&SelectDescriptor);   FD_SET(fDescriptor, &SelectDescriptor);
+	if (select(fDescriptor+1, &SelectDescriptor, NULL, NULL, &WaitTime)==-1) {
+      m->Message(m->ERROR, "Error with select() (%s)", strerror(errno));
+	  goto ExitCommunicate;
+	}
+
+	// Time-out expired?
+	if (!FD_ISSET(fDescriptor, &SelectDescriptor)) {
+  	  Data.push_back(-1);
+	  goto ExitCommunicate;
+	}
+
+	// Read data   
+	if ((N = read(fDescriptor, Buffer, sizeof(Buffer))) == -1) {
+      m->Message(m->ERROR, "Read error (%s)", strerror(errno));
+      ErrorCount++;
+	  goto ExitCommunicate;
+	}
+
+	// Add data to buffer
+	for (int i=0; i<N; i++) Data.push_back(Buffer[i]);
+  } while(Data.size() < (unsigned int) Bytes);
+  
+  // === Check if multiple of three bytes were returned ===
+  if (Data.size() % 3 != 0) {
+    Data.clear();
 	goto ExitCommunicate;
   }
 
-  // Time-out expired?
-  if (!FD_ISSET(fDescriptor, &SelectDescriptor)) {
-  	Ret = -1;
-	goto ExitCommunicate;
-  }
-
-  // Read data   
-  if ((N = read(fDescriptor, ReadBuffer, sizeof(ReadBuffer))) == -1) {
-    m->Message(m->ERROR, "Read error (%s)", strerror(errno));
-    ErrorCount++;
-	goto ExitCommunicate;
-  }
-
-  // === Check wrap counter if three bytes were returned ===
-  if (N == 3) {
+  // === Check/update all wrap counter ===  
+  for (unsigned int i=0; i<Data.size(); i+=3) {
 	if (WrapCount != -1) {
-	  if ((WrapCount+1)%8 == ((ReadBuffer[0]>>4) & 7)) WrapOK = true;
+	  if ((WrapCount+1)%8 == ((Data[i]>>4) & 7)) WrapOK = true;
 	  else WrapOK = false;
 	}
-	WrapCount = (ReadBuffer[0]>>4) & 7;
-	Ret = 1;
-  }
-  
+	WrapCount = (Data[i]>>4) & 7;
+  }
+ 
   // === UnLock file descriptor ===
   ExitCommunicate:
-  if (lockf(fDescriptor, F_ULOCK, 0) == -1) {
-	m->Message(m->ERROR, "Failed to unlock file descriptor (%s)", strerror(errno));
-	return 0;
-  }
-  
-  return Ret;
+  funlockfile(File);
+
+  if (Data.empty()) Data.push_back(0);
+
+  return Data;
 }
 
@@ -156,11 +176,12 @@
   
   unsigned char wbuf[] = {0,0,0};
-  int ret;
-  
-  if((ret = Communicate(wbuf, 3)) == 1) {
+  vector<char> Data = Communicate(wbuf, 3);
+  
+  if (Data.size() == 3) {
     ClearVoltageArrays();
     ErrorCount = 0;
-  }
-  return ret;
+	return 1;
+  }
+  return Data[0];
 }
 
@@ -181,13 +202,48 @@
 
   // Execute command
-  unsigned char wbuf[] = {1<<5 | Board<<1 | (Channel&16)>>4, Channel<<4, 0};
-  int ret;  
+  unsigned char wbuf[] = {1<<5 | Board<<1 | (Channel&16)>>4, Channel<<4, 0}; 
+  vector<char> Data = Communicate(wbuf, 3);
+
+  if (Data.size() == 3) {
+	Current[Board][Channel] = Data[1] + (Data[0]&15)*256;
+	OC[Board][Channel] = Data[0] & 128;
+	ResetHit = Data[2] & 128;
+	if (Board==2 && Channel==19) OC[Board][Channel] = false;
+	return 1;
+  }
+  return Data[0];
+}
+
+
+//
+// Read all channels status
+//
+int Crate::ReadAll() {
+
+  unsigned char wbuf[3*MAX_NUM_BOARDS*NUM_CHANNELS];
+  int Count = 0;
+
+  // Prepare command to read all channels
+  for (int i=0; i<MAX_NUM_BOARDS; i++) for (int j=0; j<NUM_CHANNELS; j++) {
+	wbuf[Count++] = 1<<5 | i<<1 | (j&16)>>4;
+	wbuf[Count++] = j<<4;
+	wbuf[Count++] = 0;
+  }
+  
+  // Execute command
+  vector<char> Data = Communicate(wbuf, 3*MAX_NUM_BOARDS*NUM_CHANNELS);
     
-  if ((ret = Communicate(wbuf, 3)) == 1) {
-	Current[Board][Channel] = ReadBuffer[1] + (ReadBuffer[0]&15)*256;
-	OC[Board][Channel] = ReadBuffer[0] & 128;
-	ResetHit = ReadBuffer[2] & 128;
-  }
-  return ret;
+  if (Data.size() != 3*MAX_NUM_BOARDS*NUM_CHANNELS) return Data[0];
+
+  //Evaluate returned data
+  Count = 0;
+  for (int i=0; i<MAX_NUM_BOARDS; i++) for (int j=0; j<NUM_CHANNELS; j++) {
+	Current[i][j] = Data[Count+1] + (Data[Count]&15)*256;
+	OC[i][j] = Data[Count] & 128;
+	ResetHit = Data[Count+2] & 128;
+	Count += 3;
+	if (i==2 && j==19) OC[i][j] = false;
+  }
+  return 1;
 }
 
@@ -204,15 +260,14 @@
   // Execute command
   unsigned char wbuf[] = {1<<6 , SetPoint>>8, SetPoint};
-  int ret;  
-
-  if ((ret = Communicate(wbuf, 3)) == 1) {
-	for (int i=0; i<MAX_NUM_BOARDS; i++) {
-      for (int j=0; j<NUM_CHANNELS; j++) {
-	    DAC[i][j] = SetPoint;
-		Volt[i][j] = (double) SetPoint / 0xfff * 90;
-	  }
-	}
-  }
-  return ret;
+  vector<char> Data = Communicate(wbuf, 3);
+
+  if (Data.size() == 3) {
+	for (int i=0; i<MAX_NUM_BOARDS; i++) for (int j=0; j<NUM_CHANNELS; j++) {
+	  DAC[i][j] = SetPoint;
+	  Volt[i][j] = (double) SetPoint / 0xfff * 90;
+	}
+	return 1;
+  }
+  return Data[0];
 }
 
@@ -237,11 +292,37 @@
   // Execute command
   unsigned char wbuf[] = {3<<5 |  Board<<1 | (Channel&16)>>4, Channel<<4 | SetPoint>>8, SetPoint};
-  int ret;  
+  vector<char> Data = Communicate(wbuf, 3);
     
-  if ((ret = Communicate(wbuf, 3)) == 1) {
+  if (Data.size() == 3) {
     DAC[Board][Channel] = SetPoint;
 	Volt[Board][Channel] = (double) SetPoint / 0xfff * 90;
-  }
-  return ret;
+	return 1;
+  }
+  return Data[0];
+}
+
+
+// ***** Channel set *****
+int Crate::SetAll() {
+  
+  unsigned char wbuf[3*MAX_NUM_BOARDS*NUM_CHANNELS];
+  double Volt = 10;
+  int Count=0;
+  for (int i=0; i<MAX_NUM_BOARDS; i++) for (int j=0; j<NUM_CHANNELS; j++) {
+	wbuf[Count++] = 3<<5 |  i<<1 | (j&16)>>4;
+	wbuf[Count++] = j<<4 | ((int) (Volt/90*0x0fff))>>8;
+	wbuf[Count++] = (int) (Volt/90*0x0fff);
+	Volt += 0.2;
+  }
+  
+  // Execute command
+  vector<char> Data = Communicate(wbuf, 3*MAX_NUM_BOARDS*NUM_CHANNELS);
+    
+  if (Data.size() == 3*MAX_NUM_BOARDS*NUM_CHANNELS) {
+    //DAC[Board][Channel] = SetPoint;
+	//Volt[Board][Channel] = (double) SetPoint / 0xfff * 90;
+	return 1;
+  }
+  return Data[0];
 }
 
@@ -251,11 +332,30 @@
   
   unsigned char wbuf = 0;
-  int Trial = 0, ret;
-  
+  int Trial = 0;
+  vector<char> Data;
+
   while(++Trial <= 3) {
-    if((ret = Communicate(&wbuf, 1)) == 1) return true;
-    if (ret == 0) break;
+    Data = Communicate(&wbuf, 1);
+    if (Data.size() == 3) return true;
+    if (Data[0] == 0) break;
   }
   return false;
+}
+
+
+// ***** Determine offset for current measurement *****
+bool Crate::CurrentCalib(double Voltage) {
+
+  // Set voltage of all channels and wait for current to settle
+  if (GlobalSet((int) (Voltage/90*0xfff)) != 1) return false;
+  sleep(1);
+  
+  // Measure current of all channels
+  if (ReadAll() != 1) return false;
+
+  for (int i=0; i<MAX_NUM_BOARDS; i++) for (int j=0; j<NUM_CHANNELS; j++) {
+ 	CurrentOffset[i][j] = Current[i][j];
+  }
+  return true;
 }
 
Index: fact/BIASctrl/Crate.h
===================================================================
--- fact/BIASctrl/Crate.h	(revision 10049)
+++ fact/BIASctrl/Crate.h	(revision 10052)
@@ -9,4 +9,5 @@
 #include <sys/ioctl.h>
 #include <string>
+#include <vector>
 
 #include "dis.hxx"
@@ -22,9 +23,9 @@
     class User *m;
 	int CrateNumber;
-	int fDescriptor; 
+	int fDescriptor;
+	FILE *File;
 	DimService *NameService;
-	char ReadBuffer[3];
 
-	int Communicate(unsigned char*, int);
+	std::vector<char> Communicate(unsigned char*, int);
 	void ClearVoltageArrays();
    
@@ -45,7 +46,10 @@
 	int DAC[MAX_NUM_BOARDS][NUM_CHANNELS];      // Voltage in DAC units
 	double Volt[MAX_NUM_BOARDS][NUM_CHANNELS];  // Voltage in Volt
+	int CurrentOffset[MAX_NUM_BOARDS][NUM_CHANNELS]; // Offset for current measurement
 
 	bool InitOK;
 
+	int SetAll();
+	int ReadAll();
 	int SystemReset();
 	int ReadChannel(unsigned int, unsigned int);
@@ -53,4 +57,5 @@
 	int ChannelSet(int, int, unsigned int);
 	bool Synch();
+	bool CurrentCalib(double);
 };
 
Index: fact/BIASctrl/History.txt
===================================================================
--- fact/BIASctrl/History.txt	(revision 10049)
+++ fact/BIASctrl/History.txt	(revision 10052)
@@ -2,2 +2,3 @@
 			program 'hvcontrol' (revision 9852).
 20/8/2010	Removed the possibility to set DAC values.
+17/11/2010	Added possibility for bulk transfers (ReadAll(), SetAll())
Index: fact/BIASctrl/User.cc
===================================================================
--- fact/BIASctrl/User.cc	(revision 10049)
+++ fact/BIASctrl/User.cc	(revision 10052)
@@ -4,4 +4,5 @@
 
 #include "User.h"
+#include <readline/readline.h>
 
 using namespace std;
@@ -17,12 +18,13 @@
     {"hv", &User::cmd_hv, 2, "<id>|<ch>|<all> <v>", "Change bias of pixel or (all) chan. of active boards"},
     {"gs", &User::cmd_gs, 1, "[crate] <volt>", "Global voltage set"},
-	{"status", &User::cmd_status, 0, "[dac]", "Show status information (DAC values if requested)"},
+	{"status", &User::cmd_status, 0, "[dac|current]", "Show status information (DAC values if requested)"},
+	{"ccal", &User::cmd_ccal, 1, "<volt>", "Calibrate current measurement at given voltage"},
 	{"load", &User::cmd_load, 1, "<file>", "Load and set bias settings from file"},
 	{"save", &User::cmd_save, 1, "<file>", "Save current bias settings to file"},
-	{"exit", &User::cmd_exit, 0, "", "Exit program"},
 	{"rate", &User::cmd_rate, 1, "<rate>", "Set refresh rate in Hz"},
 	{"timeout", &User::cmd_timeout, 1, "<time>", "Set timeout to return from read in seconds"},
 	{"reset", &User::cmd_reset, 1, "<crates>", "Reset crates"},
-	{"help", &User::cmd_help, 0, "", "Print help"}};
+	{"help", &User::cmd_help, 0, "", "Print help"},
+	{"exit", &User::cmd_exit, 0, "", "Exit program"}};
 
 
@@ -77,5 +79,5 @@
 //
 User::~User() {
-  
+
   // Wait for thread to quit
   if (pthread_join(Thread, NULL) != 0) {
@@ -170,20 +172,24 @@
 	else {
       vector<string> T = Tokenize(Parameter[n], "/");
+	  Crt.Min = 0;
+	  Crt.Max = Crates.size()-1;
+	  Chan.Min = 0;
+	  Chan.Max = MAX_NUM_BOARDS*NUM_CHANNELS-1;
+	  
 	  if (T.size() == 2) {
-		Crt = ConvertToRange(T[0]);
-		Chan = ConvertToRange(T[1]);
+		if(!ConvertToRange(T[0], Crt) || !ConvertToRange(T[1], Chan)) {
+		  PrintMessage("Numeric conversion or out-of-range error for parameter %d, skipping channel\n", n);
+		  continue;
+		}
 	  }
 	  else {
 		Crt.Min = Crt.Max = 0;
-		Chan = ConvertToRange(T[0]);
+		if (!ConvertToRange(T[0], Chan)) {
+		  PrintMessage("Numeric conversion or out-of-range error for parameter %d, skipping channel\n", n);
+		  continue;
+		}
 	  }
 	}
   
-	// Check validity of ranges
-    if (Crt.Min < 0 || Chan.Min < 0 || Crt.Max >= (int) Crates.size() || Chan.Max >= MAX_NUM_BOARDS*NUM_CHANNELS) {
-	  PrintMessage("Numeric conversion or out-of-range error for parameter %d, skipping channel\n", n);
-	  continue;
-	}
-
 	// Convert voltage value and check format 
 	if (!ConvertToDouble(Parameter[n+1], &Double)) {
@@ -304,7 +310,13 @@
 void User::cmd_reset() {
 
-  struct Range R = ConvertToRange(Parameter[1]);
-  
-  for (int i=0; i<(int) Crates.size(); i++) if (i>= R.Min && i<=R.Max) {
+  struct Range R = {0, Crates.size()-1};
+
+  // Check ranges  
+  if(!ConvertToRange(Parameter[1], R)) {
+	PrintMessage("Error, crate number out of range\n");
+	return;
+  }
+  
+  for (int i=R.Min; i<=R.Max; i++) {
 	if (Crates[i]->SystemReset() == 1) PrintMessage("System reset of crate %s (#%d)\n", Crates[i]->Name, i);
 	else PrintMessage("Error: Could not reset board %s (#%d)\n", Crates[i]->Name, i);
@@ -322,6 +334,27 @@
 
   if (Crates[0]->GlobalSet((int) (Voltage/90*0xfff)) != 1) {
-    printf("Error: Could not global set board %d\n", 0);
+    PrintMessage("Error: Could not global set board %d\n", 0);
   }    
+}
+
+//
+// Determine current measurement offset
+//
+void User::cmd_ccal() {
+
+  double Voltage;
+    
+  if (!ConvertToDouble(Parameter[1], &Voltage)) {
+    PrintMessage("Error with format of voltage parameter\n");  
+	return;
+  }
+
+  // Execute current offset determination
+  if (!Crates[0]->CurrentCalib(Voltage)) {
+    PrintMessage("Error with current calibration of board %d\n", 0);
+	return;
+  }
+  
+  PrintMessage("Current calibration of board %d done\n", 0);  
 }
 
@@ -370,9 +403,13 @@
 		Crates[i]->ResetHit ? "yes" : "no", Crates[i]->ErrorCount);
 
+	if (Parameter.size() == 1) PrintMessage("Channel voltages (in V)");
+    else if (Match(Parameter[1], "dac")) PrintMessage("Channel voltages (in DAC values)");
+	else PrintMessage("Channel currents (in uA)");
+
     for (int j=0; j<MAX_NUM_BOARDS*NUM_CHANNELS; j++) {
 	  if (j%12 == 0) PrintMessage("\n%3.1d:  ", j);
-	  if (Parameter.size() == 2) PrintMessage("%5d ", Crates[i]->DAC[j/NUM_CHANNELS][j%NUM_CHANNELS]);
-      else PrintMessage("%#5.2f ",Crates[i]->Volt[j/NUM_CHANNELS][j%NUM_CHANNELS]);
-	  //PrintMessage(" (%#5.2f %s)  ", Crates[i]->Current[j/NUM_CHANNELS][j%NUM_CHANNELS], Crates[i]->OC[j/NUM_CHANNELS][j%NUM_CHANNELS] ? "OC":"");
+      if (Parameter.size() == 1) PrintMessage("%#5.2f ",Crates[i]->Volt[j/NUM_CHANNELS][j%NUM_CHANNELS]);
+	  else if (Match(Parameter[1], "dac")) PrintMessage("%5d ", Crates[i]->DAC[j/NUM_CHANNELS][j%NUM_CHANNELS]);
+	  else PrintMessage("%#5.2f %s ", (Crates[i]->Current[j/NUM_CHANNELS][j%NUM_CHANNELS]-Crates[i]->CurrentOffset[j/NUM_CHANNELS][j%NUM_CHANNELS])*1.2, Crates[i]->OC[j/NUM_CHANNELS][j%NUM_CHANNELS] ? "OC":"");
     }
 	PrintMessage("\n");
@@ -402,5 +439,6 @@
 
   ExitRequest = true;
-  pthread_kill(Thread, SIGUSR1); 
+  //pthread_kill(Thread, SIGUSR1); 
+  //pthread_cancel(Thread); 
 }
   
@@ -421,7 +459,7 @@
  
   // Print to console
-  if(strlen(Text)>0 && Text[strlen(Text)-1]=='\n') printf("\r%s%s", Text, "\rBias> "); // New prompt
-  else printf("%s", Text);
+  printf("%s", Text); // New prompt
   fflush(stdout);
+  if (strlen(Text)>0 && Text[strlen(Text)-1]=='\n') rl_on_new_line(); // New prompt
 
   // Send to DIM text service
@@ -458,5 +496,5 @@
 
   static bool Warned = false;
-return;
+
   while (!ExitRequest) {
 	for (unsigned int i=0; i<Crates.size(); i++) {
@@ -470,5 +508,6 @@
 
       if (Crates[i]->ResetHit) {
-    	Message(INFO, "Manual reset of board %d", i);
+    	Message(INFO, "Manual reset of board %d, setting voltages to zero and issuing system reset", i);
+		Crates[i]->GlobalSet(0);
 		Crates[i]->SystemReset();
       }
@@ -478,11 +517,13 @@
       }
 
+      if (Crates[i]->ReadAll() != 1) {
+    	Message(ERROR, "Monitor could not read status from crate %d", i);
+		continue;
+      }
+
       for (int j=0; j<MAX_NUM_BOARDS*NUM_CHANNELS; j++) {
-    	if (Crates[i]->ReadChannel(j/NUM_CHANNELS, j%NUM_CHANNELS) != 1) {
-    	  Message(ERROR, "Monitor could not read status of crate %d, board %d, channel %d", i, j/NUM_CHANNELS, j%NUM_CHANNELS);
-		  continue;
-    	}
     	if (Crates[i]->OC[j/NUM_CHANNELS][j%NUM_CHANNELS]) {
-		  Message(WARN, "Overcurrent on crate %d, board %d, channel %d, resetting board", i, j/NUM_CHANNELS, j%NUM_CHANNELS);
+		  Message(WARN, "Overcurrent on crate %d, board %d, channel %d, setting voltage to zero", i, j/NUM_CHANNELS, j%NUM_CHANNELS);
+		  Crates[i]->ChannelSet(j/NUM_CHANNELS, j%NUM_CHANNELS, 0);
 		  Crates[i]->SystemReset();
     	}
@@ -505,5 +546,5 @@
 // Check if two strings match (min 1 character must match)
 //
-bool Match(string str, const char *cmd) {
+bool User::Match(string str, const char *cmd) {
 
   return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true;
@@ -514,5 +555,5 @@
 //
 // Return false if conversion did not stop on whitespace or EOL character
-bool ConvertToDouble(string String, double *Result) {
+bool User::ConvertToDouble(string String, double *Result) {
 
   char *EndPointer;
@@ -523,5 +564,5 @@
 }
 
-bool ConvertToInt(string String, int *Result) {
+bool User::ConvertToInt(string String, int *Result) {
 
   char *EndPointer;
@@ -535,26 +576,28 @@
 // Interprets a range
 //
-struct Range ConvertToRange(string String) {
-
-  struct Range R;
+bool User::ConvertToRange(string String, struct User::Range &R) {
+
+  int N, M;
 
   // Full range
-  if (Match(String, "all")) {
-    R.Min = 0;
-	R.Max = std::numeric_limits<int>::max();
-	return R;
-  }
+  if (Match(String, "all")) return true;
 
   // Single number
-  if (ConvertToInt(String, &R.Min)) {
-	R.Max = R.Min;
-	return R;
+  if (ConvertToInt(String, &N)) {
+	if (N>= R.Min && N<=R.Max) {
+	  R.Max = R.Min = N;
+	  return true;
+	}
+	return false;
   }
   
   // Range a-b
   vector<string> V = EvidenceServer::Tokenize(String, "-");
-  if (V.size() == 2 && ConvertToInt(V[0], &R.Min) && ConvertToInt(V[1], &R.Max)) return R;
-  
-  R.Min = R.Max = -1;  
-  return R;
-}
+  if (V.size()==2 && ConvertToInt(V[0], &N) && ConvertToInt(V[1], &M) && N>=R.Min && M<=R.Max) {
+	R.Min = N;
+	R.Max = M;
+	return true;
+  }
+  
+  return false;
+}
Index: fact/BIASctrl/User.h
===================================================================
--- fact/BIASctrl/User.h	(revision 10049)
+++ fact/BIASctrl/User.h	(revision 10052)
@@ -35,4 +35,14 @@
 	void commandHandler();
 
+	struct Range {
+	  int Min;
+	  int Max;
+	};
+
+	bool Match(std::string, const char *);
+	bool ConvertToDouble(std::string, double *);
+	bool ConvertToInt(std::string, int *);
+	bool ConvertToRange(std::string, struct Range &);
+
  public:
 	double fTimeOut;
@@ -53,16 +63,6 @@
 	void cmd_exit();	void cmd_rate();
 	void cmd_timeout();	void cmd_reset();
-	void cmd_help();
+	void cmd_help();	void cmd_ccal();
 };
 
-struct Range {
-  int Min;
-  int Max;
-};
-
-bool Match(std::string, const char *);
-bool ConvertToDouble(std::string, double *);
-bool ConvertToInt(std::string, int *);
-struct Range ConvertToRange(std::string);
-
 #endif
