Index: /drsdaq/DAQReadout.cc
===================================================================
--- /drsdaq/DAQReadout.cc	(revision 225)
+++ /drsdaq/DAQReadout.cc	(revision 226)
@@ -64,18 +64,18 @@
 //
  
-DAQReadout::DAQReadout():
-			DimCommand((char *) SERVER_NAME"/Command", (char *) "C"),
-			EvidenceServer(SERVER_NAME) {
-
-  // Global class pointer
+DAQReadout::DAQReadout(): EvidenceServer(SERVER_NAME) {
+
+  // Initialization
   This = this;
   MainThread = getpid();
-
-  // Start time of DAQ
+  ConsoleText = NULL;
   time(&StartTime);
+
+  // DIM console service used in PrintMessage()
+  ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
 
   // Initialize mutex for thread synchronisation
   if (pthread_mutex_init(&Mutex, NULL) != 0) {
-    State(FATAL, "pthread_mutex_init() failed");
+    Message(FATAL, "pthread_mutex_init() failed");
   }
 
@@ -89,5 +89,5 @@
   LastBoard	     		= -1;
   MinDelay  			= 1;
-  
+
   // Get configuration data
   fRawDataPath = GetConfig("RawDataPath");
@@ -145,4 +145,7 @@
   // Create instance of HV feedback (must be called after board detection)
   HVFB	  = new HVFeedback(this);  
+
+  // Install DIM command (after all initialized)
+  Command = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
 }
 
@@ -153,6 +156,7 @@
 DAQReadout::~DAQReadout() {
 
-  delete EventService;
-  delete[] DIMEventData;
+  delete Command;
+
+  delete EventService;	delete[] DIMEventData;
   delete RHeader;		delete EHeader;
   delete HVFB;			delete[] ACalibTemp;
@@ -160,7 +164,9 @@
   delete[] DRSFreq; 	delete[] BStruct;
   delete[] WaveForm;	delete[] TriggerCell;
-  
-  // Destroy mutex
-  if (pthread_mutex_destroy(&Mutex) != 0) State(ERROR, "pthread_mutex_destroy() failed");
+
+  delete ConsoleOut;
+  free(ConsoleText);
+  
+  if (pthread_mutex_destroy(&Mutex) != 0) Message(ERROR, "pthread_mutex_destroy() failed");
 }
 
@@ -171,26 +177,41 @@
 void DAQReadout::Execute(char *Command) {
 
-  if (strlen(Command)==0) return;  // Ignore empty commands
-
-  if(Command[0]=='.') {   // Shell command
+  if (Command[0]=='.') {   // Shell command
     system(&(Command[1]));
     return;
   }
 
-  for(int i=0; i<MAX_NUM_TOKEN; i++) Param[i] = "";  // All pointers point initially to empty string
+  for (int i=0; i<MAX_NUM_TOKEN; i++) Param[i] = "";  // All pointers point initially to empty string
   NParam = ParseInput(Command, Param);
-
-  for(CmdNumber=0; CmdNumber<sizeof(CommandList)/sizeof(CL_Struct); CmdNumber++)
-    if (Match(Param[0], CommandList[CmdNumber].Name)) {
-      if(CommandList[CmdNumber].NeedNotBusy && daq_state==active) PrintMessage("DAQ is busy\n");
-      else if(CommandList[CmdNumber].NeedNotBusy && NumBoards==0) PrintMessage("No boards available\n");
-      else {
-		pthread_mutex_lock(&Mutex);
-	    (this->*CommandList[CmdNumber].CommandPointer)();
-		pthread_mutex_unlock(&Mutex);
-	  }
-      return;  
-    }
-  PrintMessage("Unknown command '%s'\n",Param[0]);
+  
+  // Search for command
+  unsigned int Count;
+  for (Count=0; Count<sizeof(CommandList)/sizeof(CL_Struct); Count++) {
+    if (Match(Param[0], CommandList[Count].Name)) break;
+  }
+  
+  // Command not found?
+  if (Count == sizeof(CommandList)/sizeof(CL_Struct)) {
+	PrintMessage("Unknown command '%s'\n", Param[0]);
+	return;
+  }
+
+  if(CommandList[Count].NeedNotBusy && daq_state==active) PrintMessage("DAQ is busy\n");
+  else if(CommandList[Count].NeedNotBusy && NumBoards==0) PrintMessage("No boards available\n");
+  else {
+	int Ret;
+
+	// Lock (Execute() runs in thread spawned by commandHandler())
+	if ((Ret = pthread_mutex_lock(&Mutex)) != 0) {
+	  Message(FATAL, "pthread_mutex_lock() failed (%s)", strerror(Ret));
+  	}
+	// Run command
+	CmdNumber = Count;
+	(this->*CommandList[CmdNumber].CommandPointer)();
+	// Unlock
+	if ((Ret = pthread_mutex_unlock(&Mutex)) != 0) {
+	  Message(FATAL, "pthread_mutex_unlock() failed (%s)", strerror(Ret));
+  	}
+  }
 }
   	  
@@ -199,5 +220,5 @@
   time_t ActualT;
   time (&ActualT);
-  PrintMessage("%d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60);
+  PrintMessage("%02d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60);
 } 
 
@@ -1116,14 +1137,6 @@
   }
   
-  // Event data (It is required that at least three chunks can be written with writev(), therefore
-  // IOV_MAX>=3 is checked at startup
-  
-
-  // First chunk: trigger cells
-  //DataPart[Count].iov_base = (char *) TriggerCell + FirstBoard*RHeader->NChips*sizeof(int); // TriggerCell is without cast a pointer to an 8-byte unit (two ints) !
-  //DataPart[Count++].iov_len = RHeader->NBoards * RHeader->NChips * sizeof(int);
-  //Size += DataPart[Count-1].iov_len;
-
-  // Remaining chunks: ADC data (two chucks per channel if wrap around of pipeline occurred)
+  // ADC data (two chucks per channel if wrap around of pipeline occurred, therefore
+  // IOV_MAX>=2 is checked at startup
   for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) {
     for (unsigned int k=0; k<RHeader->NChips; k++) {
@@ -1202,30 +1215,24 @@
 void DAQReadout::DoPrintMessage(const char *Format, va_list ArgumentPointer, int Target) {
 
-  static char Textbuffer[MAX_COM_SIZE];
-  
-  memset(Textbuffer, 0, sizeof(Textbuffer));  
-  vsnprintf(Textbuffer, sizeof(Textbuffer), Format, ArgumentPointer);
+  static char Error[] = "vasprintf() failed in DoPrintMessage()";
+  char *Text;
+
+  // Evaluate arguments    
+  if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
   
   // Print to console
   if(Target & MsgToConsole) {
-    if(strlen(Textbuffer)>0 && Textbuffer[strlen(Textbuffer)-1]=='\n') {
-      printf("\r%s%s", Textbuffer, Prompt);   // New prompt
-    }
-    else printf("%s", Textbuffer);
+    if(strlen(Text)>0 && Text[strlen(Text)-1]=='\n') printf("\r%s%s", Text, Prompt);  // New prompt
+    else printf("%s", Text);
 	fflush(stdout);
   }
   
-  // Send to DIM service
-  SetStdOut(Textbuffer); 
-
-  // Send to log
-  if(Target & MsgToLog) {
-    char *Buf;
-    if (asprintf(&Buf, "%s %s", SERVER_NAME, Textbuffer) != -1) { 
-      DimClient::sendCommandNB("DColl/Log", Buf);
-      free(Buf);
-    }
-    else DimClient::sendCommandNB("DColl/Log", SERVER_NAME" asprintf() failed"); 
-  }
+  // Send to DIM console service and to log if requested
+  ConsoleOut->updateService(Text);
+  if(Target & MsgToLog) SendToLog("%s %s", SERVER_NAME, Text);
+
+  // Free old text
+  if (ConsoleText != Error) free(ConsoleText);
+  ConsoleText = Text; 
 }
 
@@ -1233,7 +1240,12 @@
 void DAQReadout::commandHandler() {
 
-  // Copy command to new buffer (must be freed by the new thread)
+  // Ignore empty or illegal strings
+  if (getCommand()->getSize() == 0 ||
+  	  *((char *) getCommand()->getData()+getCommand()->getSize()-1) != '\0' ||
+	  strlen(getCommand()->getString()) == 0) return;
+
+  // Copy command to new buffer (will be freed by global Execute() function)
   char *Command;
-  if (asprintf(&Command, "%s", getString()) == -1) {
+  if (asprintf(&Command, "%s", getCommand()->getString()) == -1) {
 	PrintMessage("asprintf() failed in DRSReadout::commandHandler() (%s)\n", strerror(errno));
 	return;
@@ -1314,8 +1326,6 @@
       else if (daq_runtype == pedestal) StopDRS();   // ..or for software trigger
 
-      // Read event data
+      // Read event data and restart (reduces dead-time because waiting for next trigger while writing)
 	  ReadCalibratedDRSData();
-	  
-	  // Restart here reduces dead-time (already waiting for next trigger while writing)
 	  StartDRS();
 
@@ -1386,8 +1396,4 @@
   else PrintMessage("\rRun #%d (%s) aborted due to error after %d events\n", RunNumber, daq_runtype_str[daq_runtype], NumEvents);
 
-  // Write run summary to slow data file
-  //m->SlowDataClass->NewEntry("Runinfo");
-  //m->SlowDataClass->AddToEntry("%d %s %s %d %d %s", m->RunNumber, WriteError?"Error":"OK", daq_runtype_str[m->daq_runtype], m->NumEvents, m->FileNumber, m->RHeader->Description);
-
   // Print run statistics
   if (NumEvents>0 && !WriteError) {
@@ -1445,4 +1451,5 @@
 }
 
+// Stub to call Execute() metho of class and free command memory
 void Execute(char *Command) {
 
Index: /drsdaq/DAQReadout.h
===================================================================
--- /drsdaq/DAQReadout.h	(revision 225)
+++ /drsdaq/DAQReadout.h	(revision 226)
@@ -33,11 +33,14 @@
 enum runtype_enum {data, pedestal, reserved, test};
 
-class DAQReadout : public DRS, public DRSCallback, public DimCommand, public EvidenceServer {
+class DAQReadout : public DRS, public DRSCallback, public EvidenceServer {
 
-    time_t StartTime;
+	time_t StartTime;
 	pid_t MainThread;
 	DimService *EventService;
 	int MinDelay;
     unsigned int CmdNumber;
+	DimCommand *Command;
+	DimService *ConsoleOut;
+	char *ConsoleText;
 
     void PrintUsage();
Index: /drsdaq/HVFeedback.cc
===================================================================
--- /drsdaq/HVFeedback.cc	(revision 225)
+++ /drsdaq/HVFeedback.cc	(revision 226)
@@ -25,6 +25,5 @@
 // Constructor: Initialise feedback 
 //
-HVFeedback::HVFeedback(DAQReadout* DAQClass){//:
-			//EvidenceServer(SERVER_NAME){
+HVFeedback::HVFeedback(DAQReadout* DAQClass){
 
   m = DAQClass;
@@ -139,5 +138,5 @@
   }
   
-  // Update DIM service regularly
+  // Update DIM count service regularly
   if (time(NULL)-LastServiceUpdate > 2) {
     LastServiceUpdate = time(NULL);
@@ -148,4 +147,6 @@
 
   // Feedback action
+  std::stringstream Cmd;
+  
   for (i=m->FirstBoard; i<=m->LastBoard; i++) {
   for (j=0; j<fNumberOfChips; j++) {
@@ -165,6 +166,7 @@
 
 		if(fabs(Average[i][j][k]) < 2*Sigma[i][j][k]) printf("Too noisy!\n");
-		else WriteHVCommand("hv %s %+f\n",PixMap->DRS_to_Pixel(i,j,k).c_str(), Correction);
-
+		else {
+		  Cmd << PixMap->DRS_to_Pixel(i,j,k)+" " << std::showpos << Correction << " ";
+		}
 	    break;
 
@@ -175,5 +177,7 @@
 	  case FB_ResponseFirst:  // First point of response measurement done  
 	    Buffer[i][j][k] = Average[i][j][k];
-	    if(!PixMap->DRS_to_Pixel(i,j,k).empty()) WriteHVCommand("hv %s %+f",PixMap->DRS_to_Pixel(i,j,k).c_str(), DiffVoltage);
+	    if(!PixMap->DRS_to_Pixel(i,j,k).empty()) {
+		  Cmd << PixMap->DRS_to_Pixel(i,j,k) << " " << std::showpos << DiffVoltage << " ";		  
+		}
 	    break;
 
@@ -184,5 +188,7 @@
 	    }
 	    else Response[i][j][k] = DiffVoltage/(Buffer[i][j][k]-Average[i][j][k]);
-	    if(!PixMap->DRS_to_Pixel(i,j,k).empty()) WriteHVCommand("hv %s %+f",PixMap->DRS_to_Pixel(i,j,k).c_str(), -DiffVoltage/2);
+	    if(!PixMap->DRS_to_Pixel(i,j,k).empty()) {
+		  Cmd << PixMap->DRS_to_Pixel(i,j,k) << " " << std::showpos << -DiffVoltage/2 << " ";		  		  
+		}
 	    break;
 
@@ -196,4 +202,9 @@
   FeedbackAverage->updateService();
   FeedbackSigma->updateService();
+
+  // Send command
+  if (!Cmd.str().empty()) {
+	DimClient::sendCommand("Bias/Command", ("hv "+Cmd.str()).c_str());
+  }
 
   switch (FBMode) {
@@ -269,6 +280,6 @@
   else {
     FBMode = Mode;
-	if (Mode != FB_ResponseFirst) m->State(m->INFO, "%s", FBState_Description[FBMode]);
-	else m->State(m->INFO, "%s (voltage difference %.3f)", FBState_Description[FBMode], DiffVoltage);
+	if (Mode != FB_ResponseFirst) m->Message(m->INFO, "%s", FBState_Description[FBMode]);
+	else m->Message(m->INFO, "%s (voltage difference %.3f)", FBState_Description[FBMode], DiffVoltage);
     ClearAverages();
   }
@@ -320,4 +331,6 @@
 void HVFeedback::MeasureResponse(float U) {
 
+  std::stringstream Cmd;
+
   if (U==0) {
     m->PrintMessage("HV Feedback: Error, voltage difference must not non-zero.\n");
@@ -325,11 +338,19 @@
   }
 
-  for (int i=m->FirstBoard; i<=m->LastBoard; i++) 
-    for (int j=0; j<fNumberOfChips; j++) 
-      for (int k=0; k<fNumberOfChannels; k++) {
-		if(!PixMap->DRS_to_Pixel(i,j,k).empty()) {
-          WriteHVCommand("hv %s %+f\n",PixMap->DRS_to_Pixel(i,j,k).c_str(), -U/2);
-    	}
-      }
+  for (int i=m->FirstBoard; i<=m->LastBoard; i++) { 
+  for (int j=0; j<fNumberOfChips; j++) { 
+  for (int k=0; k<fNumberOfChannels; k++) {
+	if(!PixMap->DRS_to_Pixel(i,j,k).empty()) {
+	  Cmd << PixMap->DRS_to_Pixel(i,j,k) << " " << std::showpos << -U/2 << " ";		  		  
+    }
+  }
+  }
+  }
+  
+  // Send command
+  if (!Cmd.str().empty()) {
+	DimClient::sendCommand("Bias/Command", ("hv "+Cmd.str()).c_str());
+  }
+
   DiffVoltage = U;
   SetFBMode(FB_ResponseFirst, true);
@@ -351,18 +372,4 @@
 
 //
-// Write bias voltage commmand
-//
-bool HVFeedback::WriteHVCommand(const char *Format, ...) {
-
-  char Textbuffer[MAX_COM_SIZE];
-  
-  va_list ArgumentPointer;  va_start(ArgumentPointer, Format); 
-  vsnprintf(Textbuffer, sizeof(Textbuffer), Format, ArgumentPointer);
-
-  DimClient::sendCommand("Bias/Command", Textbuffer);
-  return true;
-}
-
-//
 // Print feedback configuration
 //
Index: /drsdaq/HVFeedback.h
===================================================================
--- /drsdaq/HVFeedback.h	(revision 225)
+++ /drsdaq/HVFeedback.h	(revision 226)
@@ -6,4 +6,5 @@
 #include <stdlib.h>
 #include <math.h>
+#include <sstream>
 
 #include "RawDataCTX.h"
@@ -12,5 +13,5 @@
 enum FBState {FB_Off, FB_Active, FB_Targets, FB_ResponseFirst, FB_ResponseSecond};
 
-class HVFeedback: public DimServer {//EvidenceServer {
+class HVFeedback: public DimServer {
 
     class DAQReadout *m;
@@ -70,5 +71,4 @@
     void GetResponse();
     void ClearAverages();
-    bool WriteHVCommand(const char *, ...);
     void PrintConfig(int);
 };
Index: /drsdaq/drsdaq.cpp
===================================================================
--- /drsdaq/drsdaq.cpp	(revision 225)
+++ /drsdaq/drsdaq.cpp	(revision 226)
@@ -36,15 +36,6 @@
   int LockDescriptor;
 
-  // Readline library uses getc() (allows interruption by signal)
-  rl_getc_function = getc;
-
-  // writev() in DAQ thread needs to be able to write at least 3 chunks
-  if(IOV_MAX < 3) {
-	printf("Fatal error: IOV_MAX is less than 3, cannot use writev() to write event data.\n");
-	exit(EXIT_FAILURE);
-  }
-   
   // Assure only one instance of program runs (lock creator written to log file)
-  // Lock file deleted by ExitFunction()
+  // Lock file deleted by destructor
   if((LockDescriptor = open(LOCKFILE,O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1) {
     if(errno==EEXIST) {
@@ -59,13 +50,24 @@
   sprintf(str,"echo Created >%s; date >>%s; echo by $USER@$HOSTNAME >>%s",LOCKFILE,LOCKFILE,LOCKFILE);
   system(str);
-  
+
+  // Readline library uses getc() (allows interruption by signal)
+  rl_getc_function = getc;
+
+  // writev() in DAQ thread needs to be able to write at least 3 chunks
+  if(IOV_MAX < 2) {
+	printf("Fatal error: IOV_MAX is less than 2, cannot use writev() to write event data.\n");
+	exit(EXIT_FAILURE);
+  }
+     
   system("clear");
   printf("\n*** DRS readout (built %s, %s, revision %s) *** \n\n",__DATE__, __TIME__, REVISION);
+
+  // Set exit function
+  atexit(&ExitFunction);
 
   // Construct main instance (static ensures destructor is called with exit())
   static DAQReadout M;
 
-  // Set signal and exit handlers
-  atexit(&ExitFunction);
+  // Set signal handlers
   signal(SIGILL, &CrashHandler);
   signal(SIGABRT, &CrashHandler);
@@ -88,5 +90,4 @@
     // Process command
 	DimClient::sendCommand(SERVER_NAME"/Command", Command);
-
     free(Command);
   }  
@@ -94,12 +95,8 @@
 
 
-//*****************
-//  Signal handlers
-//*****************
-
 // Remove lock file before running default signal code
 void CrashHandler(int Signal) {
   remove(LOCKFILE);
-  printf("Caught signal number %d. Removing lockfile and performing standard signal action. Good luck.\n",Signal);
+  printf("Caught signal number %d. Removed lockfile and performing standard signal action. Good luck.\n",Signal);
   signal(Signal, SIG_DFL);
   raise(Signal);
@@ -109,8 +106,6 @@
 void ExitFunction() {
 
-  if (remove(LOCKFILE)==-1) {
+  if (remove(LOCKFILE) == -1) {
     printf("Could not remove lock file %s (%s)\n", LOCKFILE, strerror(errno));
   }
-
-  return;          
 }
Index: /hvcontrol/History.txt
===================================================================
--- /hvcontrol/History.txt	(revision 225)
+++ /hvcontrol/History.txt	(revision 226)
@@ -1,20 +1,20 @@
 8/7/2009    Changed path to pixelmap in ProcessIO.h to stay within repository
     	    Removed unused makefiles
-	    Moved config file to config directory of repository
-	    Removed writing of config file
-	    Removed several unused functions
-	    Removed command to switch off logging
+	    	Moved config file to config directory of repository
+	    	Removed writing of config file
+	    	Removed several unused functions
+	    	Removed command to switch off logging
 9/7/2009    Implemented readline library for command-line editing
     	    Included logging functions in main class (solved lockfile problem)
-	    Only a single log file is written
+	    	Only a single log file is written
 10/7/2009   Removed reference voltage related code (not supported by hardware)   
 17/7/2009   Communication with FDTI now with Linux kernel driver (virtual com
     	    port) instead of FTDI library (Standard open()/read()/write()
-	    system calls can be used).
+	    	system calls can be used).
 20/7/2009   Reading with time-out from tty uses now select()
 22/7/2009   Combined code of 'hv' and 'hvdiff' commands. With either one, pixel id,
     	    channel number of keyword 'all' can be used. Removed configuration
-	    option to set usage of DAC values. Instead, a third parameter 'dac'
-	    to the 'hv' and 'hvdiff' commands chooses DAC values.
+	    	option to set usage of DAC values. Instead, a third parameter 'dac'
+	    	to the 'hv' and 'hvdiff' commands chooses DAC values.
 24/7/2009   Lockfile is deleted if configuration file cannot be read
 27/7/2009   Slow data is written for every HV ramp to file given in configuration
@@ -23,5 +23,5 @@
 3/8/2009    Removed explicit test mode. Commands 'hv' and 'hvdiff' will simply
     	    return OK if no HV boards available.
-	    Number of HV boards is not limited anymore. 
+	    	Number of HV boards is not limited anymore. 
 24/9/2009   Program is terminated if too many errors are encountered by the
     	    monitor thread
@@ -35,2 +35,4 @@
 29/1/2010	DAC value 0 is now equivilant to calibrated voltage value 0
 4/6/2010	Command 'hv' allows an array of pixel IDs/voltages (used by feedback)
+15/6/2010	'Channels' now is counted continously from 0 to NUM_CHAINS*NUM_CHANNELS,
+			removed 'chain' command
Index: /hvcontrol/hvcontrol.cpp
===================================================================
--- /hvcontrol/hvcontrol.cpp	(revision 225)
+++ /hvcontrol/hvcontrol.cpp	(revision 226)
@@ -23,5 +23,4 @@
 
 // Function prototypes
-void ConsoleCommand(ProcessIO *);
 void HVMonitor(ProcessIO *);
 void DummyHandler(int);
@@ -32,11 +31,9 @@
 //   Main program
 // ================
-//
-// Several unlikely system call failures are handled via throwing an exception.
 
-int main(int argc, char *argv[]) {
+int main() {
 
-  char str[MAX_COM_SIZE];
-  pthread_t thread_ConsoleCommand,thread_HVMonitor;
+  char str[MAX_COM_SIZE], *Command;
+  pthread_t thread_HVMonitor;
   int LockDescriptor;
 
@@ -72,10 +69,6 @@
   atexit(&ExitFunction);
   
-  // Construct main instance and create mutex for thread synchronization
-  ProcessIO pio;
-  if (pthread_mutex_init(&pio.control_mutex, NULL) != 0) {
-    perror("pthread_mutex_init failed");
-    throw;
-  }
+  // Construct main instance
+  static ProcessIO M;
   
   // These signals were set during construction of EvidenceServer
@@ -85,74 +78,31 @@
   signal(SIGTERM, &CrashHandler);
 
-  // Create threads
-  if ((pthread_create(&thread_ConsoleCommand, NULL, (void * (*)(void *)) ConsoleCommand,(void *) &pio)) != 0) {
-    perror("pthread_create failed with console thread");
-    throw;
+  // Create monitor thread and make accessible for sending signal
+  if ((pthread_create(&thread_HVMonitor, NULL, (void * (*)(void *)) HVMonitor,(void *) &M)) != 0) {
+    M.Message(M.FATAL, "pthread_create failed with HVMonitor thread");
   }
-  if ((pthread_create(&thread_HVMonitor, NULL, (void * (*)(void *)) HVMonitor,(void *) &pio)) != 0) {
-    perror("pthread_create failed with HVMonitor thread");
-    throw;
-  }
+  M.HVMonitor = thread_HVMonitor;
 
-  // Threads should be accessible for sending signals
-  pio.HVMonitor = thread_HVMonitor;
-
-  // Wait for threads to quit
-  pthread_join(thread_ConsoleCommand, NULL);
-  pthread_join(thread_HVMonitor, NULL);
-
-  // Destruct mutex and main instance
-  pthread_mutex_destroy (&pio.control_mutex);
-  pio.~ProcessIO();
-
-  // Remove lockfile
-  if (remove(LOCKFILE)==-1) {
-    sprintf(str, "Could not remove lock file %s", LOCKFILE);
-    perror(str);
-    exit(EXIT_FAILURE);
-  }
-
-  exit(EXIT_SUCCESS);
-}
-
-
-/********************************************************************\
-
-  ConsoleCommand thread
-
-  Handle console input using readline library functions to allow
-  line editing and history capability
-
-\********************************************************************/
-
-void ConsoleCommand(ProcessIO *m) {
-
-  char *Command;
-
-  while (!m->Exit) {
-        
+  while (!M.ExitRequest) {        
     // Assemble prompt
-    snprintf(m->Prompt, sizeof(m->Prompt),"\rHV");
-    if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> "); 
+    if (M.NumHVBoards == 0) snprintf(M.Prompt, sizeof(M.Prompt), "\rBias> "); 
     else { 
-      if (m->FirstChain == m->LastChain) sprintf(m->Prompt+strlen(m->Prompt),"|C%d",m->FirstChain); 
-      else sprintf(m->Prompt+strlen(m->Prompt),"|C%d-%d",m->FirstChain,m->LastChain); 
-
-      if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> "); 
-      else if (m->FirstBoard == m->LastBoard) sprintf(m->Prompt+strlen(m->Prompt),"|B%d> ",m->FirstBoard); 
-      else snprintf(m->Prompt,sizeof(m->Prompt),"\rDAQ|B%d-%d> ",m->FirstBoard,m->LastBoard); 
+      if (M.FirstBoard == M.LastBoard) snprintf(M.Prompt, sizeof(M.Prompt), "\rBias|B%d> ",M.FirstBoard); 
+      else snprintf(M.Prompt, sizeof(M.Prompt),"\rBias|B%d-%d> ",M.FirstBoard, M.LastBoard); 
     }
 
     // Read Command
-    Command = readline(m->Prompt);
-    if (Command==NULL) {
-      m->PrintMessage("Error reading command line input\n");
-      continue;
-    }
+    Command = readline(M.Prompt);
+    if (Command == NULL) continue;
     if(strlen(Command)>0) add_history(Command);
 
-    // Process command (via DIM gives automatic serialisation)
+    // Process command (via DIM gives automatic thread serialisation)
 	DimClient::sendCommand("Bias/Command", Command);
     free(Command);
+  }
+
+  // Wait for thread to quit
+  if (pthread_join(thread_HVMonitor, NULL) != 0) {
+    M.PrintMessage("pthread_join() failed in main()");
   }
 }
@@ -161,19 +111,13 @@
 /********************************************************************\
 
-  HVMonitor
-
   Monitor HV board status
-  Sebastian Commichau, November 2008
 
 \********************************************************************/
 
 void HVMonitor(ProcessIO *m) {
-  
-  while (!m->Exit) {
-    if (m->state == active) {
-      pthread_mutex_lock(&m->control_mutex);
-      m->Monitor();
-      pthread_mutex_unlock(&m->control_mutex);	
-    }
+
+ 
+  while (!m->ExitRequest) {
+    if (m->state == active) m->Monitor();
     usleep((unsigned long)floor(1000000./(m->NumHVBoards*m->fStatusRefreshRate)));
   }
@@ -202,5 +146,7 @@
 // This function will be implicitly called by exit()
 void ExitFunction() {
-  remove(LOCKFILE);
-  return;          
+
+  if (remove(LOCKFILE) == -1) {
+    printf("Could not remove lock file %s (%s)\n", LOCKFILE, strerror(errno));
+  }
 }
Index: /hvcontrol/src/ProcessIO.cc
===================================================================
--- /hvcontrol/src/ProcessIO.cc	(revision 225)
+++ /hvcontrol/src/ProcessIO.cc	(revision 226)
@@ -22,6 +22,5 @@
   } CommandList[] = 
    {{"board", &ProcessIO::cmd_board, 1, "<i>|<i j>|<all>" ,"Address board i, boards i-j or all boards or list boards"},
-	{"chain", &ProcessIO::cmd_chain, 1, "<i>|<i> <j>|<all>","Address chain i, chains i-j or all chains"},
-	{"hv", &ProcessIO::cmd_hv, 2, "<id>|<ch>|<all> <v>", "Change bias of pixel, ch. or all ch. of active chain(s)/board(s)"},
+	{"hv", &ProcessIO::cmd_hv, 2, "<id>|<ch>|<all> <v>", "Change bias of pixel or (all) chan. of active boards"},
 	{"status", &ProcessIO::cmd_status, 0, "[dac]", "Show status information (DAC values if requested)"},
 	{"config", &ProcessIO::cmd_config, 0, "", "Print configuration"},
@@ -40,4 +39,6 @@
 using namespace std;
 
+
+// Constructor
 ProcessIO::ProcessIO(): EvidenceServer(SERVER_NAME) {
 
@@ -46,12 +47,18 @@
 
   // Initialize status variables
-  state     = active;
-  Exit      = false;
-  
+  state       = active;
+  ConsoleText = NULL;
+
   NumHVBoards = 0;
   FirstBoard  = 0;
   LastBoard   = -1;
-  FirstChain  = 0;
-  LastChain   = NUM_CHAINS-1;
+
+  // Initialize mutex for thread synchronisation
+  if (pthread_mutex_init(&Mutex, NULL) != 0) {
+    Message(FATAL, "pthread_mutex_init() failed");
+  }
+
+  // DIM console service used in PrintMessage()
+  ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
 
   // Get configuration data
@@ -94,4 +101,5 @@
 
 
+// Destructor
 ProcessIO::~ProcessIO() {
   
@@ -101,5 +109,11 @@
   delete[] fHVBoard;
     
-  delete pm;	    delete calib;
+  delete pm;
+  delete calib;
+  delete ConsoleOut;	
+  free(ConsoleText);
+  
+  // Destroy mutex
+  if (pthread_mutex_destroy(&Mutex) != 0) Message(ERROR, "pthread_mutex_destroy() failed");
 }
 
@@ -180,24 +194,4 @@
 } 
 
-// Adress chains
-void ProcessIO::cmd_chain() {
-    
-  if (!NumHVBoards) return;
-
-  if (Match(Parameter[1],"all")) {
-    FirstChain = 0;
-    LastChain = 3;
-  } 
-  else if (Parameter.size()==2 && atoi(Parameter[1].c_str())>=0 && atoi(Parameter[1].c_str())<NUM_CHAINS) {
-    FirstChain = atoi(Parameter[1].c_str());
-    LastChain = FirstChain;
-  }
-  else if (Parameter.size()==3 && atoi(Parameter[1].c_str())>=0 && atoi(Parameter[1].c_str())<NUM_CHAINS && 
-         atoi(Parameter[2].c_str())>0 && atoi(Parameter[2].c_str())<NUM_CHAINS) {
-    FirstChain = atoi(Parameter[1].c_str());
-    LastChain = atoi(Parameter[2].c_str());
-  }
-  else PrintMessage("Cannot address chain(s), out of range.\n");
-} 
 
 // Print configuration
@@ -209,6 +203,5 @@
   for (int i=0; i<NumHVBoards; i++) PrintMessage(" Board %d: %s\n", i, fHVBoard[i]->BoardName);
 
-  PrintMessage( " TimeOut:           %.2f s\n"
-    	    	" StatusRefreshRate: %.2f Hz\n"
+  PrintMessage(	" StatusRefreshRate: %.2f Hz\n"
     	    	" DACMin value:      %d\n"
     	    	" DACMax value:      %d\n"
@@ -216,5 +209,5 @@
     	    	" HVCalibSlope :     %f\n"
     	    	" HVMaxDiff :        %u\n",
-		fTimeOut, fStatusRefreshRate, DACMin,
+		fStatusRefreshRate, DACMin,
 		DACMax, fHVCalibOffset, fHVCalibSlope, fHVMaxDiff);
 }
@@ -236,83 +229,65 @@
 void ProcessIO::cmd_hv() {
 
-  int hvoltage, DACValue, Errors=0, Board=-1, Chain=-1, Channel=-1;
-  double hvoltageV;
+  int Int, SingleChannel; 
+  unsigned int DACValue, Errors=0;
+  double Double;
   bool SetDac;
 
-  // If array of channels, evaluate here (used by DIM)
-  if (Parameter.size()>3 && pm->Pixel_to_HVboard(Parameter[3]) != 999999999) {
-	for (unsigned int n=1; n<Parameter.size()-1; n+=2) {
-	  if (pm->Pixel_to_HVboard(Parameter[n]) != 999999999) {
-      	Board = pm->Pixel_to_HVboard(Parameter[n]);
-      	Chain = pm->Pixel_to_HVchain(Parameter[n]);
-    	Channel = pm->Pixel_to_HVchannel(Parameter[n]);
-		hvoltageV = atof(Parameter[n+1].c_str());
-		if (isdigit(Parameter[n+1][0])==0) fHVBoard[Board]->HVV[Chain][Channel] += hvoltageV;
-	    else fHVBoard[Board]->HVV[Chain][Channel] = hvoltageV;
-	    DACValue = calib->HVToDAC(fHVBoard[Board]->HVV[Chain][Channel], Board, Chain, Channel);
-
-	  }
-	}	
-    return;
-  }
-
-  // Evaluate voltage parameter
-  if (Parameter.size()==4 && Match(Parameter[3], "dac")) {
-    SetDac = true;
-    if (!ConvertToInt(Parameter[2], &hvoltage)) {
-      PrintMessage("Error: Wrong number format for DAC voltage setting\n");
-      return;
-    } 
-  }
-  else {
-    SetDac = false;
-    if (!ConvertToDouble(Parameter[2], &hvoltageV)) {
-      PrintMessage("Error: Wrong number format for voltage setting\n");
-      return;
-    }
-  } 
-
-  // Evaluate pixel or channel parameter    
-  if(pm->Pixel_to_HVboard(Parameter[1]) != 999999999) {
-    Board = pm->Pixel_to_HVboard(Parameter[1]);
-    Chain = pm->Pixel_to_HVchain(Parameter[1]);
-    Channel = pm->Pixel_to_HVchannel(Parameter[1]);
-  }
-  else if (!Match(Parameter[1], "all") && !ConvertToInt(Parameter[1], &Channel)) {
-    PrintMessage("Error: Wrong channel identification\n");
-    return;
-  }
-
+  // Interpretation as DAC value requested?
+  if (Parameter.size() == 4 && Match(Parameter[3], "dac")) SetDac = true;
+  else SetDac = false;
+
+  // Interprete first number (might be channel number)
+  if (!ConvertToInt(Parameter[1], &SingleChannel)) SingleChannel = -1;
+
+  // Loop over all boards
   for (int i=FirstBoard; i<=LastBoard; i++) {
-    if (i!=Board && Board!=-1) continue;
-  for (int j=FirstChain; j<=LastChain; j++) {
-      if (j!=Chain && Chain!=-1) continue;
-  for (int k=0; k<NUM_CHANNELS; k++) {
-	if (k!=Channel && Channel!=-1) continue;
-
-	// Voltage change ignored if DAC value is zero
-	if (isdigit(Parameter[2][0])==0 && fHVBoard[i]->HV[j][k] == 0) continue;
-
-	// Determine new DAC values
-	if (!SetDac){
-	  if (isdigit(Parameter[2][0])==0) fHVBoard[i]->HVV[j][k] += hvoltageV; // voltage change
-	  else fHVBoard[i]->HVV[j][k] = hvoltageV;
-	  DACValue = calib->HVToDAC(fHVBoard[i]->HVV[j][k], i, j, k);
-	}
-	else {
-	  if (isdigit(Parameter[2][0])==0) DACValue = fHVBoard[i]->HV[j][k] + hvoltage; // voltage change
-	  else DACValue = hvoltage;
-	}
-
-	// Set new voltage (if DAC value, update calibrated value)
-	if (!RampVoltage(DACValue, i, j, k)) Errors++;
-	if (SetDac) fHVBoard[i]->HVV[j][k] = calib->DACToHV(fHVBoard[i]->HV[j][k], i, j, k);
-  } // Channels	
-  } // Chains
-
-  // Update DIM service
-  fHVBoard[i]->BiasVolt->updateService();
+	// Loop over all channels given as command parameter
+ 	for (unsigned int n=1; n<Parameter.size()-1; n+=2) {
+	  // Loop over all channels
+	  for (int j=0; j<NUM_CHAINS; j++) for (int k=0; k<NUM_CHANNELS; k++) {
+
+		// Current channel must be considered?
+		if (!Match(Parameter[1], "all") && !(i == (int) pm->Pixel_to_HVboard(Parameter[n]) &&
+			j == (int) pm->Pixel_to_HVchain(Parameter[n]) && k == (int) pm->Pixel_to_HVchannel(Parameter[n])) &&
+			!(j == SingleChannel/NUM_CHANNELS && k == SingleChannel%NUM_CHANNELS)) continue;
+
+		// Voltage change (number starts with + oder -) ignored if current DAC value is zero
+		if (isdigit(Parameter[n+1][0])==0 && fHVBoard[i]->HV[j][k] == 0) continue;
+
+		// Set new voltage/DAC value
+		if (!SetDac){
+		  // Convert voltage value and check format 
+		  if (!ConvertToDouble(Parameter[n+1], &Double)) {
+			PrintMessage("Error: Wrong number format for voltage setting\n");
+			goto LeaveLoop;
+		  }
+		  // Adjust voltage and DAV value
+		  if (isdigit(Parameter[n+1][0]) == 0) fHVBoard[i]->HVV[j][k] += Double;
+		  else fHVBoard[i]->HVV[j][k] = Double;
+		  DACValue = calib->HVToDAC(fHVBoard[i]->HVV[j][k], i, j, k);
+		}
+		else {
+		  // Convert DAC value and check format 
+		  if (!ConvertToInt(Parameter[n+1], &Int)) {
+			PrintMessage("Error: Wrong number format for DAC voltage setting\n");
+			goto LeaveLoop;
+		  } 
+		  // Adjust DAC value
+		  if (isdigit(Parameter[n+1][0]) == 0) DACValue = fHVBoard[i]->HV[j][k] + Int;
+		  else DACValue = Int;
+		}
+
+		// Set new voltage (if DAC value, update calibrated value)
+		if (!RampVoltage(DACValue, i, j, k)) Errors++;
+		if (SetDac) fHVBoard[i]->HVV[j][k] = calib->DACToHV(fHVBoard[i]->HV[j][k], i, j, k);
+	  } // Channels and chains	
+	} // Loop over command argument
+
+	// Update DIM service for this boar
+	LeaveLoop:
+	fHVBoard[i]->BiasVolt->updateService();
   } // Boards
-
+  
   if (Errors > 0) PrintMessage("Errors on %d channel(s) occurred\n", Errors);
 }
@@ -454,21 +429,16 @@
   for (int i=FirstBoard; i<=LastBoard; i++) {
     PrintMessage(" BOARD %d (%s)   Wrap counter: %s (%d)  Manual reset: %s\n    Time-out: %.2f s  Error count: %d\n\n",
-  fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName,
-  fHVBoard[i]->WrapOK ? "ok":"error",
-  fHVBoard[i]->LastWrapCount, 
-  fHVBoard[i]->ResetButton ? "yes" : "no",
-  fHVBoard[i]->fTimeOut,
-  fHVBoard[i]->ErrorCount);
-
-    for (int j=FirstChain; j<=LastChain; j++) {
+		fHVBoard[i]->GetBoardNumber(), fHVBoard[i]->BoardName,
+		fHVBoard[i]->WrapOK ? "ok":"error",	fHVBoard[i]->LastWrapCount, 
+		fHVBoard[i]->ResetButton ? "yes" : "no", fHVBoard[i]->fTimeOut, fHVBoard[i]->ErrorCount);
+
+    for (int j=0; j<NUM_CHAINS; j++) {
 	  PrintMessage("  CHAIN %d     Over-current: %s\n", j, fHVBoard[i]->Overcurrent[j] ? "yes" : "no");
-	  for (int k=0;k<4;k++) {
-        PrintMessage("\r");
-	    for (int l=0;l<8;l++) {
-		  if(Parameter.size() == 2) PrintMessage("%5d ",fHVBoard[i]->HV[j][k*8+l]);
-    	  else PrintMessage("%#5.2f ",fHVBoard[i]->HVV[j][k*8+l]);
-    	}
-	    PrintMessage("\n");
-	  }
+	  for (int l=0; l<NUM_CHANNELS; l++) {
+		if (l%8 == 0) PrintMessage("\r%3.1d:  ", j*NUM_CHANNELS+l);
+		if (Parameter.size() == 2) PrintMessage("%5d ",fHVBoard[i]->HV[j][l]);
+    	else PrintMessage("%#5.2f ",fHVBoard[i]->HVV[j][l]);
+		if (l%8 == 7) PrintMessage("\n");
+      }
     }
   }
@@ -508,5 +478,5 @@
 void ProcessIO::cmd_exit() {
 
-  Exit = true;
+  ExitRequest = true;
   pthread_kill(HVMonitor, SIGUSR1); 
 }
@@ -535,30 +505,24 @@
 void ProcessIO::DoPrintMessage(const char *Format, va_list ArgumentPointer, MsgTarget Target) {
 
-  static char Textbuffer[MAX_COM_SIZE]; // static because of DIM service
-
-  memset(Textbuffer, 0, sizeof(Textbuffer));  
-  vsnprintf(Textbuffer, sizeof(Textbuffer), Format, ArgumentPointer);
+  static char Error[] = "vasprintf() failed in DoPrintMessage()";
+  char *Text;
+
+  // Evaluate arguments    
+  if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
   
   // Print to console
   if(Target & Console) {
-    if(strlen(Textbuffer)>0 && Textbuffer[strlen(Textbuffer)-1]=='\n') {
-      printf("\r%s%s", Textbuffer, Prompt);   // New prompt
-    }
-    else printf("%s", Textbuffer);
+    if(strlen(Text)>0 && Text[strlen(Text)-1]=='\n') printf("\r%s%s", Text, Prompt); // New prompt
+    else printf("%s", Text);
     fflush(stdout);
   }
 
-  // Send to DIM service
-  SetStdOut(Textbuffer); 
-
-  // Send to log
-  if(Target & Log) {
-    char *Buf;
-    if (asprintf(&Buf, "%s %s", SERVER_NAME, Textbuffer) != -1) { 
-      DimClient::sendCommandNB("DColl/Log", Buf);
-      free(Buf);
-    }
-    else DimClient::sendCommandNB("DColl/Log", SERVER_NAME" asprintf() failed"); 
-  }
+  // Send to DIM console service and log
+  ConsoleOut->updateService(Text); 
+  if(Target & Log) SendToLog("%s %s", SERVER_NAME, Text);
+
+  // Free old text
+  if (ConsoleText != Error) free(ConsoleText);
+  ConsoleText = Text; 
 }
 
@@ -572,5 +536,5 @@
 
     if (fHVBoard[Board]->SetHV(Chain, Channel, fHVBoard[Board]->HV[Chain][Channel]+Diff) != 1) {
-      State(ERROR, "Could not set bias of board %d, chain %d, channel %d. Skipping channel\n",fHVBoard[Board]->GetBoardNumber(),Chain,Channel);
+      Message(ERROR, "Could not set bias of board %d, chain %d, channel %d. Skipping channel\n",fHVBoard[Board]->GetBoardNumber(),Chain,Channel);
       return false;
     }
@@ -585,5 +549,11 @@
 
   static bool Warned = false;
-  
+  int Ret;
+
+  // Lock because command execution runs in different thread
+  if ((Ret = pthread_mutex_lock(&Mutex)) != 0) {
+	Message(FATAL, "pthread_mutex_lock() failed in commandHandler() (%s)", strerror(Ret));
+  }
+
   for (int i=0; i<NumHVBoards; i++) {
     if (fHVBoard[i]->ErrorCount > 10) {
@@ -591,5 +561,5 @@
         Warned = true;
         PrintMessage(All, "Warning: Some board has many read/write errors, status monitor disabled\n");
-        State(WARN, "Warning: Some board has many read/write errors, status monitor disabled\n");
+        Message(WARN, "Warning: Some board has many read/write errors, status monitor disabled\n");
       }
       continue;
@@ -598,10 +568,10 @@
     if (fHVBoard[i]->GetStatus() != 1) {
       PrintMessage(All, "Error: Monitor could not read status of board %d\n", fHVBoard[i]->GetBoardNumber());
-      State(ERROR, "Error: Monitor could not read status of board %d\n", fHVBoard[i]->GetBoardNumber());
+      Message(ERROR, "Error: Monitor could not read status of board %d\n", fHVBoard[i]->GetBoardNumber());
     }
     
     if (fHVBoard[i]->ResetButton) {
       PrintMessage(All, "Manual reset of board %d\n",fHVBoard[i]->GetBoardNumber());
-      State(INFO, "Manual reset of board %d\n",fHVBoard[i]->GetBoardNumber());
+      Message(INFO, "Manual reset of board %d\n",fHVBoard[i]->GetBoardNumber());
       ResetBoard(i);
     }
@@ -609,5 +579,5 @@
     if (!fHVBoard[i]->WrapOK) {
       PrintMessage(All, "Error: Wrap counter mismatch board %d\n",fHVBoard[i]->GetBoardNumber());
-      State(ERROR, "Error: Wrap counter mismatch board %d\n",fHVBoard[i]->GetBoardNumber());
+      Message(ERROR, "Error: Wrap counter mismatch board %d\n",fHVBoard[i]->GetBoardNumber());
     }
 
@@ -615,9 +585,15 @@
       if (fHVBoard[i]->Overcurrent[j]) {
 		PrintMessage(All, "Warning: Overcurrent in chain %d of board %d\n",j,fHVBoard[i]->GetBoardNumber());
-		State(WARN, "Warning: Overcurrent in chain %d of board %d\n",j,fHVBoard[i]->GetBoardNumber());
+		Message(WARN, "Warning: Overcurrent in chain %d of board %d\n",j,fHVBoard[i]->GetBoardNumber());
 		ResetBoard(i);
       }
     }
   }
+  
+  // Unlock
+  if ((Ret = pthread_mutex_unlock(&Mutex)) != 0) {
+	Message(FATAL, "pthread_mutex_lock() failed in commandHandler() (%s)", strerror(Ret));
+  }
+
 }
 
@@ -651,7 +627,15 @@
 void ProcessIO::commandHandler() {
 
-  pthread_mutex_lock(&control_mutex);
+  int Ret;
+
+  if ((Ret = pthread_mutex_lock(&Mutex)) != 0) {
+	Message(FATAL, "pthread_mutex_lock() failed in commandHandler() (%s)", strerror(Ret));
+  }
+
   if (getCommand() == Command) CommandControl(getCommand()->getString());
-  pthread_mutex_unlock(&control_mutex);
+
+  if ((Ret = pthread_mutex_unlock(&Mutex)) != 0) {
+	Message(FATAL, "pthread_mutex_unlock() failed in commandHandler() (%s)", strerror(Ret));
+  }
 }
 
Index: /hvcontrol/src/ProcessIO.h
===================================================================
--- /hvcontrol/src/ProcessIO.h	(revision 225)
+++ /hvcontrol/src/ProcessIO.h	(revision 226)
@@ -33,4 +33,6 @@
 	PixelMap *pm;
 	DimCommand *Command;
+	DimService *ConsoleOut;
+	char *ConsoleText;
 
 	void commandHandler();
@@ -39,6 +41,4 @@
 	HVCalib     *calib;
 	HVBoard **fHVBoard;
-
-	pthread_mutex_t control_mutex;
 
 	char Prompt[MAX_COM_SIZE];
@@ -57,13 +57,10 @@
 	// Status variables  
 	pthread_t HVMonitor;       // exit function sends signal to these threads
+	pthread_mutex_t Mutex;
 
 	int NumHVBoards;
 	int FirstBoard;
 	int LastBoard;
-	int FirstChain;
-	int LastChain;
-  
 	state_enum   state;
-	bool Exit;
 
 	// Methods
@@ -87,5 +84,4 @@
 	void cmd_start();	void cmd_stop();
 	void cmd_uptime();	void cmd_help();
-	void cmd_chain();
 };
 
