Index: fact/BIASctrl/User.cc
===================================================================
--- fact/BIASctrl/User.cc	(revision 11362)
+++ fact/BIASctrl/User.cc	(revision 11772)
@@ -12,21 +12,23 @@
 								void (User::*CommandPointer)();
 								unsigned int MinNumParameter;
+								bool NeedCrate;
 								const char *Parameters;
 								const char *Help;
   } CommandList[] = 
-   {{"pixel", &User::cmd_hv, 2, "<pix id> <volt|default>", "Change bias of pixel (to default value)"},
-	{"channel", &User::cmd_hv, 2, "<range> <volt|default>", "Change bias of (all) channels of active crate"},
-    {"gs", &User::cmd_gs, 1, "[crate] <volt>", "Global voltage set active crate"},
-	{"reset", &User::cmd_reset, 0, "", "Reset active crate"},
-	{"synch", &User::cmd_synch, 0, "", "Synchronize active crate"},	{"crate", &User::cmd_crate, 1, "<number>", "Select active crate"},
-	{"status", &User::cmd_status, 0, "[dac|current]", "Show status information (DAC values if requested)"},
-	{"mode", &User::cmd_mode, 1, "<static|dynamic>", "Set voltage stabilization mode (experimental)"},
-	{"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"},
-	{"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"},
-	{"help", &User::cmd_help, 0, "", "Print help"},
-	{"exit", &User::cmd_exit, 0, "", "Exit program"}};
-
+   {{"pixel", &User::cmd_hv, 2, true, "<range> <voltage|default|info>", "Change bias of pixels (to default)"},
+	{"channel", &User::cmd_hv, 2, true, "<range> <voltage|default|info>", "Change bias of channels of active crate (to default)"},
+    {"gs", &User::cmd_gs, 1, true, "[crate] <volt>", "Global voltage set active crate"},
+	{"reset", &User::cmd_reset, 0, true, "", "Reset active crate"},
+	{"synch", &User::cmd_synch, 0, true, "", "Synchronize active crate"},
+	{"crate", &User::cmd_crate, 1, true, "<number>", "Select active crate"},
+	{"status", &User::cmd_status, 0, false, "[dac|current]", "Show status information (DAC values if requested)"},
+	{"mode", &User::cmd_mode, 1, false, "<static|dynamic>", "Set voltage stabilization mode (experimental)"},
+	{"load", &User::cmd_load, 1, true, "<file>", "Load and set bias settings from file"},
+	{"save", &User::cmd_save, 1, true, "<file>", "Save current bias settings to file"},
+	{"rate", &User::cmd_rate, 1, false, "<rate>", "Set refresh rate in Hz"},
+	{"timeout", &User::cmd_timeout, 1, false, "<time>", "Set timeout to return from read in seconds"},
+	{"help", &User::cmd_help, 0, false, "", "Print help"},
+	{"exit", &User::cmd_exit, 0, false, "", "Exit program"},
+	{".", &User::cmd_shell, 1, false, "<command>", "Execute shell command"}};   
 
 //
@@ -42,6 +44,5 @@
 
   // Get configuration data
-  vector<string> Boards = Tokenize(GetConfig("Boards"), " \t");
-  Boards = Tokenize("FTE00FOH", " \t");
+  vector<string> Boards = Tokenize(GetConfig("Boards", "dummy"), " \t");
   fTimeOut = atof(GetConfig("TimeOut").c_str());
   fStatusRefreshRate = atof(GetConfig("StatusRefreshRate").c_str());
@@ -70,8 +71,5 @@
     }
   }
-
-  // Set active crate
-  if (!Boards.empty()) ActiveCrate = 0;
-  else ActiveCrate = -1;
+  ActiveCrate = 0;
 
   // Create PixelMap instance (map from config server)
@@ -94,7 +92,9 @@
 User::~User() {
 
+  int Ret;
+  
   // Wait for thread to quit
-  if (pthread_cancel(Thread) != 0) Message(ERROR, "pthread_cancel() failed in ()");
-  if (pthread_join(Thread, NULL) != 0) Message(ERROR, "pthread_join() failed in ()");
+  if ((Ret = pthread_cancel(Thread)) != 0) Message(ERROR, "pthread_cancel() failed (%s)", strerror(Ret));
+  if ((Ret = pthread_join(Thread, NULL)) != 0) Message(ERROR, "pthread_join() failed (%s)", strerror(Ret));
 
   // Delete all crates
@@ -118,20 +118,32 @@
   if (getCommand() != DIMCommand || Command.size() < 2) return;
 
-  // Shell command
-  if(Command[0]=='.') {
-    if (system(Command.c_str()+1) == 1) {
-	  PrintMessage("Error with system() call\n");
-	}
-    return;
-  }
-
   // Parse command into tokens
   Parameter = Tokenize(Command, " ");
 
+  // Special handling of shell execution
+  if (Command[0] == '.') {
+    Parameter.clear();
+	Parameter.push_back(".");
+	Parameter.push_back(Command.substr(1));
+  }
+  
   // Search for command in command list
   for(unsigned int CmdNumber=0; CmdNumber<sizeof(CommandList)/sizeof(CL_Struct); CmdNumber++) {
     if (Match(Parameter[0], CommandList[CmdNumber].Name)) {
-      if(Parameter.size()-1 < CommandList[CmdNumber].MinNumParameter) {
+	  // Requested command help?
+      if (Parameter.size() == 2 && Match(Parameter[1], "?")) {
+		PrintMessage("Usage: %s %s\n%s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters, CommandList[CmdNumber].Help);
+		return;
+	  }
+
+	  // Incorrect number of parameters?
+      if (Parameter.size()-1 < CommandList[CmdNumber].MinNumParameter) {
 		PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters);
+		return;
+	  }
+
+	  // Check if crates needed
+	  if (CommandList[CmdNumber].NeedCrate && Crates.empty()) {
+		PrintMessage("No crate available\n");
 		return;
 	  }
@@ -149,13 +161,12 @@
 void User::cmd_help() {
 
-  char Buffer[MAX_COM_SIZE];
   for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
-    snprintf(Buffer, sizeof(Buffer), "%s %s", CommandList[i].Name, CommandList[i].Parameters);
-    PrintMessage("%-28s%s\n", Buffer, CommandList[i].Help);
-  }
-  
-  PrintMessage(".<command>                  Execute shell command\n\n"
+    PrintMessage("%-10s%s\n", CommandList[i].Name, CommandList[i].Help);
+  }
+  
+  PrintMessage("\nUse '?' as argument to get more extensive help.\n"
+  	"Commands 'pixel' and 'channel' allow and arbitary number of argument pairs.\n"
   	"Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n"
-    "Channel ranges can be 'all', a single number or in the form 'a-b'.\n"); 
+    "Ranges can be 'all', a single number or in the form 'a-b'.\n"); 
 } 
 
@@ -164,6 +175,4 @@
 //
 void User::cmd_synch() {
-
-  if (ActiveCrate == -1) return;  
 
   if (Crates[ActiveCrate]->Synch()) PrintMessage("Synchronized crate %d\n", ActiveCrate);
@@ -199,30 +208,43 @@
   unsigned int Errors=0;
   double Double;
-  struct Range Crt, Chan;
-  unsigned int PixelID = 0;
+  struct Range Chan, Pixel;
+  unsigned int Crate, Channel;
   vector< map<unsigned int, double> > Voltages (Crates.size());
+  vector < pair <unsigned int, unsigned int> > P;
 
   // Loop over all parameters
   for (unsigned int n=1; n < Parameter.size()-1; n+=2) {
 
+	// Convert voltage value and check format 
+	if (!ConvertToDouble(Parameter[n+1], &Double) && !Match(Parameter[n+1], "default") && !Match(Parameter[n+1], "info")) {
+	  PrintMessage("Error: Wrong number format for voltage setting\n");
+	  continue;
+	}
+
+	// Extract affected channels for this argument pair
+	P.clear();
+
 	// Pixel identification?
 	if (Match(Parameter[0], "pixel")) {
-	  // Skip if first character is not digit
-	  if (isdigit(Parameter[n][0] == 0)) continue;
+	  Pixel.Min = 0;
+	  Pixel.Max = 1439;
+
+	  if (!ConvertToRange(Parameter[n], Pixel)) {
+		PrintMessage("Pixel ID out-of-range for parameter %d, skipping channel\n", n);
+		continue;
+	  }
 	  
-	  // Extract pixel ID
-	  PixelID = atoi(Parameter[n].c_str());
-
-	  // Skip if pixel ID not existing 
-	  if (PixMap->Pixel_to_HVcrate(PixelID) == PixMap->PM_ERROR_CODE) continue;
-
-      Crt.Min = Crt.Max = PixMap->Pixel_to_HVcrate(PixelID);
-      Chan.Min = Chan.Max = PixMap->Pixel_to_HVboard(PixelID)*NUM_CHANNELS + PixMap->Pixel_to_HVchannel(PixelID);
+	  for (int i=Pixel.Min; i<=Pixel.Max; i++) {
+	    Crate = PixMap->Pixel_to_HVcrate(i);
+		Channel = PixMap->Pixel_to_HVboard(i)*NUM_CHANNELS + PixMap->Pixel_to_HVchannel(i);
+		
+		// Skip if pixel ID or corresponding channels not existing 
+		if (Crate!=PixMap->PM_ERROR_CODE && Crate<Crates.size() && Channel<MAX_NUM_BOARDS*NUM_CHANNELS) {
+		  P.push_back(make_pair(Crate, Channel));
+		}
+	  }
 	}
 	// Channel identification
 	else {
-	  if (ActiveCrate == -1) continue;
-	  else 	Crt.Min = Crt.Max = ActiveCrate;
-
 	  Chan.Min = 0;
 	  Chan.Max = MAX_NUM_BOARDS*NUM_CHANNELS-1;	  
@@ -232,38 +254,54 @@
 		continue;
 	  }
-	}
-  
-	// Convert voltage value and check format 
-	if (!ConvertToDouble(Parameter[n+1], &Double) && !Match(Parameter[n+1], "default")) {
-	  PrintMessage("Error: Wrong number format for voltage setting\n");
-	  continue;
-	}
-
-	// Loop over given crates and channels
-	for (int i=Crt.Min; i<=Crt.Max; i++) for (int j=Chan.Min; j<=Chan.Max; j++) {
-	  // Check that indices are in legal range
-	  if (i<0 || i>=(int) Crates.size() || j<0 || j >=MAX_NUM_BOARDS*NUM_CHANNELS) continue;
+	  
+	  for (int i=Chan.Min; i<=Chan.Max; i++) P.push_back(make_pair(ActiveCrate, i));	  
+	}
+  
+	// Loop over all given channels
+	for (unsigned int i=0; i<P.size(); i++) {
+	  Crate = P[i].first;
+	  Channel = P[i].second;
+
+	  // Should only information be printed?
+	  if (Match(Parameter[n+1], "info")) {
+		PrintMessage("Crate %2d, channel %3d  ", Crate, Channel);
+		if (!Crates[Crate]->Present[Channel/NUM_CHANNELS][Channel%NUM_CHANNELS]) {
+		  PrintMessage("(channel not present in crate)");
+		}
+		PrintMessage("\n  Channel is on board %d, board channel %d\n", Channel/32, Channel%32);
+
+		// Print pixel information
+		vector<unsigned int> List = PixMap->HV_to_Pixel(Crate, Channel/NUM_CHANNELS, Channel%NUM_CHANNELS);
+		PrintMessage("\n  Pixel IDs (default voltage): ");
+		for (unsigned int j=0; j<List.size(); j++) PrintMessage("%u (%.2f)   ", List[j], List[j]<DefaultVoltage.size() ? DefaultVoltage[List[j]] : 0);
+		if (List.empty()) PrintMessage("none");
+
+		// Print voltage and current
+		PrintMessage("\n  Voltage setpoint: %.2f V (DAC %u)    Current: %.2f uA ", Crates[Crate]->GetVoltage(Channel), Crates[Crate]->GetDAC(Channel), Crates[Crate]->GetCurrent(Channel));
+
+		if (Crates[Crate]->OC[Channel/NUM_CHANNELS][Channel%NUM_CHANNELS]) PrintMessage("(overcurrent)\n");
+		else PrintMessage("\n");
+
+		continue;
+	  }
+
+	  // Check that indices are in legal range (safety check, should be unnecessary)
+	  if (Crate >= Crates.size() || Channel >=MAX_NUM_BOARDS*NUM_CHANNELS) continue;
  
 	  // Voltage change (number starts with + oder -) ignored if current DAC value is zero
-	  if ((Parameter[n+1][0]=='+' || Parameter[n+1][0]=='-') && Crates[i]->GetDAC(j) == 0) continue;
-
+	  if (Parameter[n+1][0]=='+' || Parameter[n+1][0]=='-') {
+		if (Crates[Crate]->GetDAC(Channel) == 0) continue;
+	  }
+	
 	  // Should the default value be set?
 	  if (Match(Parameter[n+1], "default")) {
-	    // Pixel ID given directly or channel number?
-	    if (!Match(Parameter[0], "pixel")) {
-	      vector<unsigned int> List = PixMap->HV_to_Pixel(i, j/NUM_CHANNELS, j%NUM_CHANNELS);
-	      if (!List.empty()) PixelID = List[0];
-	      else PixelID = numeric_limits<unsigned int>::max();
-	    }
-
-            // Set default voltage
-	    if (PixelID < DefaultVoltage.size()) Voltages[i][j] = DefaultVoltage[PixelID];
-	    else Voltages[i][j] = 0;
-	    continue;
+		vector<unsigned int> List = PixMap->HV_to_Pixel(Crate, Channel/NUM_CHANNELS, Channel%NUM_CHANNELS);
+		if (!List.empty() && List[0]<DefaultVoltage.size()) Double = DefaultVoltage[List[0]];
+		else Double = 0;
 	  }
 
 	  // Relative or absolute change?
-	  if (isdigit(Parameter[n+1][0]) == 0) Voltages[i][j] = Crates[i]->GetVoltage(j) + Double;
-	  else Voltages[i][j] = Double;
+	  if (isdigit(Parameter[n+1][0]) == 0) Voltages[Crate][Channel] = Crates[Crate]->GetVoltage(Channel) + Double;
+	  else Voltages[Crate][Channel] = Double;
 	} // Channels
   } // Loop over command argument
@@ -305,5 +343,5 @@
     for (unsigned int Crate=0; Crate<Crates.size(); Crate++) if (Match(Crates[Crate]->Name, Buffer)) {
 
-	  if ((int) Crate != ActiveCrate) continue;
+	  if (Crate != ActiveCrate) continue;
 	  PrintMessage("Found bias settings for crate %s (#%d)\n\r", Crates[Crate]->Name, Crate);
 
@@ -359,9 +397,6 @@
 void User::cmd_reset() {
 
-  if (ActiveCrate == -1) return;  
-  
   if (Crates[ActiveCrate]->SystemReset() == 1) PrintMessage("System reset of crate %d\n", ActiveCrate);
   else PrintMessage("Error: Could not reset crate %d\n", ActiveCrate);
-
 }
 
@@ -372,6 +407,4 @@
 
   double Voltage;
-
-  if (ActiveCrate == -1) return;  
 
   if (!ConvertToDouble(Parameter[1], &Voltage)) {
@@ -432,5 +465,5 @@
 
   PrintMessage(" Number of crates:  %d\n", Crates.size());
-  PrintMessage(" Active crate:     %d\n", ActiveCrate);  
+  PrintMessage(" Active crate:      %d\n", ActiveCrate);  
   PrintMessage(" Refresh rate:      %.2f Hz\n", fStatusRefreshRate);
   PrintMessage(" Time out:          %.2f s\n\n", fTimeOut);
@@ -480,5 +513,13 @@
   pthread_kill(MainThread, SIGTERM);
 }
-  
+
+//
+// Execute shell command
+//
+void User::cmd_shell() {
+
+  if (system(Parameter[1].c_str()) == -1) PrintMessage("Error with system() call\n");
+}
+
 
 //
@@ -521,5 +562,4 @@
     // Remove channels already at target (check for DAC, not for floating-point voltage)
 	for (map<unsigned int, double>::iterator it = Voltages.begin(); it != Voltages.end(); ++it) {
-	  //if (Crates[Crate]->GetDAC(it->first) == (unsigned int ) (it->second/90.0*0x0fff)) Voltages.erase(it);
 	  if (fabs(Crates[Crate]->GetVoltage(it->first)-it->second) < 0.001) Voltages.erase(it);
 	}
@@ -549,4 +589,5 @@
   static bool Warned = false;
   int Ret;
+  unsigned int Count=0;
 
   while (!ExitRequest) {
@@ -592,5 +633,6 @@
       for (int j=0; j<MAX_NUM_BOARDS*NUM_CHANNELS; j++) {
     	if (Crates[i]->OC[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);
+		  if (Count++ < 5) Message(WARN, "Overcurrent on crate %d, board %d, channel %d, setting voltage to zero", i, j/NUM_CHANNELS, j%NUM_CHANNELS);
+		  if (Count == 5) Message(ERROR, "Five overcurrent warnings, no more!");
 		  Voltages[j] = 0;
     	}
