Index: /fact/FADctrl/FAD.cc
===================================================================
--- /fact/FADctrl/FAD.cc	(revision 10127)
+++ /fact/FADctrl/FAD.cc	(revision 10128)
@@ -27,5 +27,5 @@
    {"srclk", &FAD::cmd_srclk, true, 1, "<on|off>", "Set SRCLK"},
    {"sclk", &FAD::cmd_sclk, true, 1, "<on|off>", "Set SCLK"},
-   {"trigger", &FAD::cmd_trigger, false, 0, "[n|cont|stop|enable|disable]", "Issue software triggers"},
+   {"trigger", &FAD::cmd_trigger, false, 0, "[n|cont [rate]|stop|enable|disable]", "Issue software triggers"},
    {"roi", &FAD::cmd_roi, true, 2, "<channel range> <value>", "Set region-of-interest to value"},
    {"dac", &FAD::cmd_dac, true, 2, "<range> <value>", "Set DAC numbers in range to value"},
@@ -61,5 +61,6 @@
   EventUpdateDelay = atof(GetConfig("EventUpdateDelay", "0.5").c_str());
 
-  // Create pipe for data exchange
+  // Create pipe for command execution and data exchange
+  if (pipe(CommandPipe) == -1) Message(FATAL, "pipe() failed in FAD::FAD() (%s)", strerror(errno));
   if (pipe(Pipe) == -1) Message(FATAL, "pipe() failed in FAD::FAD() (%s)", strerror(errno));
 
@@ -67,4 +68,7 @@
   ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
 
+  // Initialise configuration information (later non-blocking access in commandHandler())
+  GetConfig("CalibTempDiffWarn", "0");
+
   // Construct boards
   BoardList = Tokenize(GetConfig("BoardList"));
@@ -75,5 +79,5 @@
 	// Check if initialised OK
 	if (!Boards.back()->InitOK) {
-	  Message(WARN, "Failed to initialize board %s\n", BoardList[i].c_str());
+	  Message(WARN, "Failed to initialize board %s", BoardList[i].c_str());
 	  delete Boards.back();
 	  Boards.pop_back();
@@ -83,7 +87,10 @@
   // Create DIM event service thread
   int Ret;
-  if ((Ret = pthread_create(&Thread, NULL, (void * (*)(void *)) LaunchEventThread,(void *) this)) != 0) {
+  if ((Ret = pthread_create(&Thread, NULL, (void * (*)(void *)) LaunchEventThread, (void *) this)) != 0) {
     Message(FATAL, "pthread_create() failed in FAD::FAD() (%s)", strerror(Ret));
   }
+
+  // Create command handling thread
+  DimThread::start();
 
   // Install DIM command (after all initialized)
@@ -99,8 +106,10 @@
 
   // Close pipe (will make read() on pipe in DIM service thread return)
-  if (close(Pipe[0]) == -1) Message(ERROR, "close() on Pipe[0] failed in FAD::~FAD() (%s)", strerror(errno));;
-  if (close(Pipe[1]) == -1) Message(ERROR, "close() on Pipe[1] failed in FAD::~FAD() (%s)", strerror(errno));;
-
-  // Wait for DIM service thread to quit
+  if (close(Pipe[0]) == -1) Message(ERROR, "close() on Pipe[0] failed in FAD::~FAD() (%s)", strerror(errno));
+  if (close(Pipe[1]) == -1) Message(ERROR, "close() on Pipe[1] failed in FAD::~FAD() (%s)", strerror(errno));
+  if (close(CommandPipe[0]) == -1) Message(ERROR, "close() on CommandPipe[0] failed in FAD::~FAD() (%s)", strerror(errno));
+  if (close(CommandPipe[1]) == -1) Message(ERROR, "close() on CommandPipe[1] failed in FAD::~FAD() (%s)", strerror(errno));
+
+  // Wait for command and DIM service thread to quit
   if ((Ret = pthread_join(Thread, NULL)) != 0) Message(ERROR, "pthread_join() failed in ~FAD() (%s)", strerror(Ret));
 
@@ -118,59 +127,92 @@
 
 //
-// DIM command handler
-// Handler must be non-blocking, otherwise a DIM rpc would dead-lock.
+// DIM command handler (non-blocking, otherwise a DIM rpc would dead-lock)
 //
 void FAD::commandHandler() {
 
-  char *Command = getCommand()->getString();
-
   // Ignore empty or illegal strings
-  if (getCommand()->getSize() == 0 || *(Command+getCommand()->getSize()-1) != '\0' ||
-	  strlen(Command) == 0) return;
-
-  // Shell command
-  if (Command[0]=='.') {
-    system(&(Command[1]));
-    return;
-  }
-
-  // Parse command into tokens
-  Parameter.clear();
-  char *Start; 
-  while(true) {
-    while (isspace(*Command)) Command++; // Ignore initial white spaces
-    if(*Command=='\0') break;
-    if (*Command == '\"') {
-	  Start = ++Command;
-      while(*Command!='\"' && *Command!='\0') Command++;
-    }
-    else {
-	  Start = Command;
-      while(!isspace(*Command) && *Command!='\0') Command++;
-    }
-    if(*Command != '\0') *Command++ = '\0';
-	Parameter.push_back(Start);
-  }
-
-  // Search for command in command list
-  for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
-    if (Match(Parameter[0], CommandList[i].Name)) {
-	  // Check if number of parameters
-      if(Parameter.size()-1 < CommandList[i].MinNumParameter) {
-		PrintMessage("Usage: %s %s\n", CommandList[i].Name, CommandList[i].Parameters);
-		return;
-	  }
-	  // Check if idle mode required
-	  if (CommandList[i].NeedIdle && Mode != idle) {
-		PrintMessage("Current mode is not idle ('stop' will stop current operation)\n");
-		return;
-	  }
-	  // Jump to command function
-	  (this->*CommandList[i].CommandPointer)();
-	  return;  
-    }
-  }
-  
-  PrintMessage("Unknown command '%s'\n", Parameter[0].c_str());
+  if (getCommand()->getSize() == 0 || *(getCommand()->getString()+getCommand()->getSize()-1) != '\0' ||
+	  strlen(getCommand()->getString()) == 0) return;
+
+  // Send command to command execution thread
+  if (write(CommandPipe[1], getCommand()->getData(), getCommand()->getSize()) == -1) {
+	Message(ERROR, "write() to CommandPipe[1] failed in class FAD::commandHandler() (%s)", strerror(errno));
+  }
+}
+
+//
+// Command execution thread
+//
+void FAD::threadHandler() {
+
+  char *Command, *Start, Buffer[1000];
+  int Ret;
+  unsigned int n;
+
+  while (!ExitRequest) {
+	if ((Ret=read(CommandPipe[0], Buffer, sizeof(Buffer))) == -1) Message(ERROR, "read() from CommandPipe[0] failed in FAD::threadHandler() (%s)", strerror(errno));
+
+	// Check if pipe closed
+	if (Ret == 0) break;
+
+	// Shell command
+	if (Buffer[0]=='.') {
+      system(&(Buffer[1]));
+      continue;
+	}
+
+	// Parse command into tokens
+	Parameter.clear();
+	Command = Buffer;
+	while(true) {
+      while (isspace(*Command)) Command++; // Ignore initial white spaces
+      if(*Command=='\0') break;
+      if (*Command == '\"') {
+		Start = ++Command;
+    	while(*Command!='\"' && *Command!='\0') Command++;
+      }
+      else {
+		Start = Command;
+    	while(!isspace(*Command) && *Command!='\0') Command++;
+      }
+      if(*Command != '\0') *Command++ = '\0';
+	  Parameter.push_back(Start);
+	}
+
+	// Search for command in command list
+	for(n=0; n<sizeof(CommandList)/sizeof(CL_Struct); n++) {
+      if (Match(Parameter[0], CommandList[n].Name)) break;
+	}
+
+	// Command not found?	
+	if (n == sizeof(CommandList)/sizeof(CL_Struct)) {
+	  PrintMessage("Unknown command '%s'\n", Parameter[0].c_str());
+	  continue;
+	}
+
+	// Check if number of parameters
+    if(Parameter.size()-1 < CommandList[n].MinNumParameter) {
+	  PrintMessage("Usage: %s %s\n", CommandList[n].Name, CommandList[n].Parameters);
+	  continue;
+	}
+
+	// Check if idle mode required
+	if (CommandList[n].NeedIdle && Mode != idle) {
+	  PrintMessage("Current mode is not idle ('stop' will stop current operation)\n");
+	  continue;
+	}
+	
+	// Jump to command function
+	(this->*CommandList[n].CommandPointer)();
+  } // while()
+}
+
+//
+// DIM exit handler (overwriting handler in Evidence class)
+//
+void FAD::exitHandler(int Code) {
+
+  Message(INFO, "Exit handler called (DIM exit code %d)", Code);
+  pthread_kill(MainThread, SIGTERM);
 }
 
@@ -260,14 +302,17 @@
 
   int Num;
-  
+
   for (unsigned int i=0; i<Boards.size(); i++) {
 	if (Parameter.size() == 1) Boards[i]->Send(CMD_Trigger);
 	else if (ConvertToInt(Parameter[1], &Num)) {
-	  for (int j=0; j<Num; j++) {
-	    Boards[i]->Send(CMD_Trigger);
-		usleep(10000);
+	  for (int j=0; j<Num; j++) Boards[i]->Send(CMD_Trigger);
+	}
+	else if (Match(Parameter[1],"continuous")) {
+	  Boards[i]->Send(CMD_Trigger_C);
+	  if (Parameter.size() == 3 && ConvertToInt(Parameter[2], &Num)) {
+	    if (Num == 0) Boards[i]->Send(CMD_Trigger_S);		
+		else Boards[i]->Send(0x2100 + (unsigned char) (1000.0/Num/12.5));
 	  }
 	}
-	else if (Match(Parameter[1],"continuous")) Boards[i]->Send(CMD_Trigger_C);
 	else if (Match(Parameter[1],"stop")) Boards[i]->Send(CMD_Trigger_S);
 	else if (Match(Parameter[1],"enable")) Boards[i]->Send(CMD_TRIGGERS_ON);
@@ -278,7 +323,4 @@
 	}
   }
-
-  if (Match(Parameter[1],"enable")) PrintMessage("All active boards accept incoming triggers\n");
-  else if (Match(Parameter[1],"disable")) PrintMessage("No active board accepts incoming triggers\n");
 } 
 
@@ -452,4 +494,5 @@
   struct tm *T = localtime(&Time);
   char Filename[500];
+  double Temp;
 
   // Set number of requested events
@@ -466,7 +509,21 @@
   }
 
+  // Check conditions for run of all active boards
+  float MaxDiff = atof(GetConfig("CalibTempDiffWarn").c_str());
+
+  for (unsigned int i=0; i<Boards.size(); i++) {
+	if (!Boards[i]->Active) continue;
+
+	if (Boards[i]->ACalibTime == -1) PrintMessage("Warning: Amplitude calibration missing for board %d\n", i);
+	else {
+	  Temp = 0;
+	  for (unsigned int j=0; j<NTemp; j++) Temp += Boards[i]->GetStatus().Temp[j] / NTemp;
+	  if (fabs(Boards[i]->ACalibTemp-Temp) > MaxDiff) PrintMessage("Warning: Amplitude calibration to current temperature difference larger than %.1f K for board %d\n", MaxDiff, i);
+	}
+  }
+
   // Start run
   Mode = datarun;
-  PrintMessage("Starting run with %d events, filename '%s'\n", NumEventsRequested, Filename);
+  Message(INFO, "Starting run with %d events, filename '%s'", NumEventsRequested, Filename);
 }
 
@@ -543,6 +600,7 @@
   }
 
-  // Start ca;ibration by setting mode
+  // Start calibration by setting mode
   Mode = acalib;
+  Message(INFO, "Starting amplitude calibration run with 3x%d events", NumEventsRequested);
 }
 
@@ -563,5 +621,5 @@
 	}	  
 
-	PrintMessage(" Number of FAD boards: %d    Boards with communication error: %d   Active boards: ", Boards.size(), Error);
+	PrintMessage("Total boards: %d    (%d communication errors)     Active boards: ", Boards.size(), Error);
 
 	// Print list of active boards
@@ -591,24 +649,30 @@
 	if (i<R.Min || i > R.Max) continue;
 
+	PrintMessage("BOARD #%d (%sactive)   IP %s   Communication %s\n", i, Boards[i]->Active ? "":"in", Boards[i]->Name, Boards[i]->CommError ? "ERROR":"OK");
+
+	// Calibration information
+	if (Boards[i]->ACalibTime == -1) PrintMessage("No amplitude calibration available\n");
+	else PrintMessage("Calibration temperature %.1f     Calibration time %s" , Boards[i]->ACalibTemp, ctime(&Boards[i]->ACalibTime));
+
+	// Status information
 	struct FADBoard::BoardStatus S = Boards[i]->GetStatus();
 
-	PrintMessage("Board #%d - %s (%sactive)    Communication %s\n", i, Boards[i]->Name, Boards[i]->Active ? "":"in", Boards[i]->CommError ? "ERROR":"OK");
+	if (S.Update.tv_sec == -1) {
+	  PrintMessage("No event received yet, no further status information available\n");
+	  continue; 
+	}
+	PrintMessage("Last event received %s", ctime(&S.Update.tv_sec));
+
 	PrintMessage("Board ID %d		Firmware revision %d\n", S.BoardID, S.FirmwareRevision);
 	PrintMessage("DAC %d %d %d %d   %d %d %d %d\n", S.DAC[0], S.DAC[1], S.DAC[2], S.DAC[3], S.DAC[4], S.DAC[5], S.DAC[6], S.DAC[7] );
 	PrintMessage("Temperature %.2f %.2f %.2f %.2f", S.Temp[0], S.Temp[1], S.Temp[2], S.Temp[3]);
 
-	for (unsigned int i=0; i<NChips*NChannels; i++) {
-	  if (i%NChannels == 0) PrintMessage("\nROI %2d-%2d: ", i, i+NChannels-1);
-	  PrintMessage("%4d ", S.ROI[i/NChannels][i%NChannels]);
+	for (unsigned int j=0; j<NChips*NChannels; j++) {
+	  if (j%NChannels == 0) PrintMessage("\nROI %2d-%2d: ", j, j+NChannels-1);
+	  PrintMessage("%4d ", S.ROI[j/NChannels][j%NChannels]);
 	}
 	PrintMessage("\n");
-
 	/*PrintMessage("Serial %d, firmware %d\n"
-                    " Actual temperature:   %1.1lf C\n"
-		    " Calibration temp.:    %1.1lf C\n"
 		    GetBoard(i)->GetBoardSerialNumber(),
-		    GetBoard(i)->GetFirmwareVersion(),
-		    ACalibTemp[i]);
-
 
 	if (GetBoard(i)->GetStatusReg() & BIT_RUNNING)
@@ -619,9 +683,5 @@
 	else
 	  PrintMessage("   DMODE single shot\n");
-	if (GetBoard(i)->GetCtrlReg() & BIT_ACAL_EN)
-	  PrintMessage("   ACAL enabled\n");
-	PrintMessage(" Trigger bus:          0x%08X\n", GetBoard(i)->GetTriggerBus());
-	else PrintMessage(" Domino wave stopped\n");
-      }
+ 
     }*/
   } // for()
@@ -667,5 +727,5 @@
   double Delay;
   
-  if (Parameter.size()==2 && ConvertToDouble(Parameter[1], &Delay) && Delay>0) EventUpdateDelay = Delay;
+  if (Parameter.size()==2 && ConvertToDouble(Parameter[1], &Delay) && Delay>=0) EventUpdateDelay = Delay;
   else PrintUsage();
 }
@@ -707,6 +767,9 @@
   }
   
-  if (Mode == acalib) Mode = idle;
-  
+  if (Mode == acalib) {
+	Mode = idle;
+	Message(INFO, "Mode set to IDLE");
+  }
+
   if (Mode == datarun) {  
 	// Inform event thread to stop run in case datarun active
@@ -798,10 +861,12 @@
   RunStart = LastUpdate; // only to avoid 'uninitialized' warning from compiler
   
-  // Create DIM event data service
+  // Create DIM event data and number services
   int EventSize = sizeof(RunHeader)+ Boards.size()*sizeof(BoardStructure)+sizeof(EventHeader) + Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int));
-  char *EventData = new char [EventSize]; 
-  DimService *EventService = new DimService (SERVER_NAME"/EventData", (char *) "C", NULL, 0);
+  char *EventData = new char [EventSize];
 
   memset(EventData, 0, EventSize);
+
+  DimService EventService(SERVER_NAME"/EventData", (char *) "C", NULL, 0);
+  DimService EventNumService(SERVER_NAME"/EventNumber", NumEvents);
 
   // Calculate pointers to EventData array
@@ -818,5 +883,5 @@
   RHeader->EventHeaderSize = sizeof(EventHeader);
   RHeader->BoardStructureSize = sizeof(BoardStructure);
-  RHeader->SoftwareRevision = 0xFFFF;	// Update
+  RHeader->SoftwareRevision = atoi(REVISION) * (strchr(REVISION, 'M')==NULL ? 1:-1);
   RHeader->Identification = 0;
 
@@ -841,6 +906,10 @@
     // Wait for data from TCP/IP reading threads
     if ((Ret=read(Pipe[0], Buffer, sizeof(Buffer))) == -1) Message(FATAL, "read() from Pipe[0] failed in FAD::EventThread() (%s)", strerror(errno));
+
+	// Check if pipe closed
+	if (Ret == 0) break;
+	
 	IDString = string(Buffer, Ret);
-	
+
 	// If amplitude calibration mode, check if board finished procedure
 	if (Mode == acalib) {
@@ -854,4 +923,5 @@
 	    SaveAmplitudeCalibration();
 		Mode = idle;
+		Message(INFO, "Amplitude calibration done, mode set to IDLE");
 	  }
 	}
@@ -888,5 +958,6 @@
 
 	  Datafile = -1;
-	  Mode = idle;	  
+	  Mode = idle;
+	  Message(INFO, "Data run ended, mode set to IDLE");	  
 	}
 
@@ -919,5 +990,5 @@
 	  for(unsigned int i=0; i<NChips; i++) TriggerCell[Brd*NChips+i] = (int) S.TriggerCell[i];
 
-	  // Write channel data (12 bit signed twis complement with out-of-range-bit and leading zeroes)
+	  // Write channel data (12 bit signed two's complement with out-of-range-bit and leading zeroes)
 	  int Count = 0;
 	  memset(Data, 0, Boards.size()*NChips*NChannels*NBins*sizeof(short));
@@ -949,5 +1020,6 @@
 	if ((Time.tv_sec-LastUpdate.tv_sec)*1e6 + Time.tv_usec-LastUpdate.tv_usec > EventUpdateDelay*1e6) {
 	  gettimeofday(&LastUpdate, NULL);
-	  EventService->updateService(EventData, EventSize);
+	  EventService.updateService(EventData, EventSize);
+	  EventNumService.updateService();
 	}
 	
@@ -975,8 +1047,8 @@
 	// Write data to file
 	if(write(Datafile, EventData+Offset, EventSize-Offset) != (ssize_t) EventSize-Offset) {
-	  PrintMessage("Error: Could not write all data to file, terminating run (%s)\n", strerror(errno));
+	  Message(ERROR, "Could not write all data to file, terminating run, setting mode to IDLE (%s)", strerror(errno));
 
 	  // Close file if error
-	  if (close(Datafile) == -1) PrintMessage("Error: Could not close data file (%s)\n", strerror(errno));
+	  if (close(Datafile) == -1) Message(ERROR, "Could not close data file (%s)", strerror(errno));
 	  Datafile = -1;
 	  Mode = idle;
@@ -988,8 +1060,7 @@
   }
 
-  // Clean up
   delete[] BStruct;
-  delete EventService;
   delete[] EventData;
+
 }
 
Index: /fact/FADctrl/FAD.h
===================================================================
--- /fact/FADctrl/FAD.h	(revision 10127)
+++ /fact/FADctrl/FAD.h	(revision 10128)
@@ -25,5 +25,5 @@
 
 
-class FAD: public EvidenceServer {
+class FAD: public EvidenceServer, public DimThread {
 
   public:
@@ -39,4 +39,6 @@
     void PrintUsage();
 	void commandHandler();
+	void threadHandler();
+	virtual void exitHandler(int);
 	bool Match(std::string, const char *);
 	void EventThread();
@@ -55,4 +57,5 @@
 	};
 
+	void ExecuteCommand();
 	bool ConvertToDouble(std::string, double *);
 	bool ConvertToInt(std::string, int *);
@@ -86,4 +89,5 @@
 	
 	int Pipe[2];
+	int CommandPipe[2];
     int NumEventsRequested;	// Number of events requested
 	std::vector<std::string> BoardList;
Index: /fact/FADctrl/FADBoard.cc
===================================================================
--- /fact/FADctrl/FADBoard.cc	(revision 10127)
+++ /fact/FADctrl/FADBoard.cc	(revision 10128)
@@ -427,8 +427,6 @@
 	  PrevStatus = Status;
 
-	  // Lock to avoid concurrent access in GetStatus()
+	  // Wait until event thread processed the previous data and lock to avoid concurrent access in GetStatus()
 	  Lock();
-
-	  // Wait until event thread processed the previous data
 	  while (!Continue) {
 		if ((Ret = pthread_cond_wait(&CondVar, &Mutex)) != 0) {
@@ -436,5 +434,4 @@
 		}
 	  }
-
 	  gettimeofday(&Status.Update, NULL);
 
Index: /fact/FADctrl/FADctrl.cc
===================================================================
--- /fact/FADctrl/FADctrl.cc	(revision 10127)
+++ /fact/FADctrl/FADctrl.cc	(revision 10128)
@@ -27,8 +27,8 @@
 
   // Construct main instance (static ensures destructor is called with exit())
-  static FAD M;
+  static class FAD M;
 
   // Do not kill process if writing to closed socket
-  signal(SIGPIPE,SIG_IGN);
+  signal(SIGPIPE, SIG_IGN);
 
   // Initialise all boards
@@ -43,5 +43,5 @@
   DimClient::sendCommand(SERVER_NAME"/Command", "trigger enable");
   M.PrintMessage("Finished initalizing all boards\n");
-  
+
   // Command loop
   char *Command;
Index: /fact/FADctrl/History.txt
===================================================================
--- /fact/FADctrl/History.txt	(revision 10127)
+++ /fact/FADctrl/History.txt	(revision 10128)
@@ -9,2 +9,4 @@
 24/1/2011	Integrated amplitude calibration into FADBoard class
 27/1/2011	Moved OpenOtherSockets() to FADBoard class
+2/2/2011	Warning if amplitude calibration missing for data taking
+4/2/2011	Fixed exitHandler() being stuck at pthread_join()
