Index: /fact/FADctrl/FAD.cc
===================================================================
--- /fact/FADctrl/FAD.cc	(revision 10036)
+++ /fact/FADctrl/FAD.cc	(revision 10036)
@@ -0,0 +1,1092 @@
+/********************************************************************\
+
+  FAD.cc
+
+  Main class of FADCtrl
+
+
+ Comment 19/10/2010: It is assumed that boolean access is an atomic operation.
+     
+\********************************************************************/
+
+#include "FAD.h"
+using namespace std;
+
+static const struct CL_Struct { const char *Name;    
+              void (FAD::*CommandPointer)();
+			  bool NeedIdle;
+			  unsigned int MinNumParameter;
+			  const char *Parameters;
+			  const char *Help;
+  } CommandList[] = 
+  {{"board", &FAD::cmd_board, true, 1, "[+|-]<range>" ,"Activate or deactivate boards"},
+   {"status", &FAD::cmd_status, false, 0, "[range]", "Show board status information"},
+   {"domino", &FAD::cmd_domino, true, 1, "<on|off>", "Switch Domino wave"},
+   {"dwrite", &FAD::cmd_dwrite, true, 1, "<on|off>", "Set DWRITE"},
+   {"phase", &FAD::cmd_phase, true, 1, "<phase>", "Adjust ADC phase (in 'steps')"},
+   {"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]", "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"},
+   {"address", &FAD::cmd_address, true, 2, "<range> <value>", "Set addresses in range to value"},
+   {"send", &FAD::cmd_send, true, 1, "<value>", "Set arbitrary data to board"},
+   {"acalib", &FAD::cmd_acalib, true, 0, "", "Amplitude calibration"},
+   //{"tcalib", &FAD::cmd_tcalib, 1, "<trig rate> ", "Time calibration"},
+   //{"wmode", &FAD::cmd_wmode, 0, "<run|stop>", "Domino wave running or stopped during read out"},
+   //{"rmode", &FAD::cmd_rmode, 0, "<first|stop>", "Readout start at first bin or stop position (DRS4)"},
+   //{"dmode", &FAD::cmd_dmode, 0, "<single|continuous>", "Domino wave single shot or continuous"},
+   {"cancel", &FAD::cmd_cancel, false, 0, "", "Cancel current operation"},
+   {"update", &FAD::cmd_update, false, 1, "<sec>", "Minimum delay between updates to DIM event service"},		  
+   {"exit", &FAD::cmd_exit, true, 0, "", "Exit program"},
+   {"help", &FAD::cmd_help, false, 0, "", "Print help"}};
+
+
+
+// ------------------------------------
+// *****  Constructor/Destructor  *****
+// ------------------------------------
+
+//
+// Constructor
+// 
+FAD::FAD(): EvidenceServer(SERVER_NAME) {
+
+  // Initialization
+  ConsoleText = NULL;
+  MainThread = pthread_self();
+  Mode = idle;
+  EventUpdateDelay = atof(GetConfig("EventUpdateDelay", "0.5").c_str())*1e6;
+
+  // DIM console service used in PrintMessage()
+  ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
+
+  // Get configuration data (static needed for c_str() pointers to remain valid after constructor finished)
+  //static string _CalibDataPath = GetConfig("CalibDataPath");
+  //fCalibDataPath = _CalibDataPath.c_str();
+
+  //snprintf(CalibInfoFilename,sizeof(CalibInfoFilename), "%s/CalibInfo", fCalibDataPath);
+
+  // Construct boards
+  BoardList = Tokenize(GetConfig("BoardList","192.33.99.225"));
+
+  for (unsigned int i=0; i<BoardList.size(); i++) {
+    Boards.push_back(new class FADBoard(BoardList[i], 5000, this, i));
+
+	// Check if initialised OK
+	if (!Boards.back()->InitOK) {
+	  Message(WARN, "Failed to initialize board %s\n", BoardList[i].c_str());
+	  delete Boards.back();
+	  Boards.pop_back();
+	}
+  }
+
+  // Create DIM event service thread
+  int Ret;
+  if ((Ret = pthread_create(&Thread, NULL, (void * (*)(void *)) LaunchEventThread,(void *) this)) != 0) {
+    Message(ERROR, "pthread_create() failed in FAD::FAD() (%s)", strerror(Ret));
+	Thread = pthread_self();
+  }
+
+  // Install DIM command (after all initialized)
+  Command = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
+}
+
+//
+// Destructor
+//
+FAD::~FAD() {
+
+  int Ret;
+
+  // Delete all boards (cancels threads automatically)
+  for (unsigned int i=0; i<Boards.size(); i++) delete Boards[i];
+
+  // Cancel SIM service thread and wait for it to quit
+  if (pthread_equal(Thread, pthread_self()) == 0) {
+	if ((Ret = pthread_cancel(Thread)) != 0) Message(ERROR, "Could not request thread cancellation (%s)", strerror(Ret));
+	if ((Ret = pthread_join(Thread, NULL)) != 0) Message(ERROR, "pthread_join() failed (%s)", strerror(Ret));
+  }
+
+  delete Command;
+  delete ConsoleOut;
+  free(ConsoleText);
+}
+
+// ------------------------------
+// *****  Command handling  *****
+// ------------------------------
+
+//
+// DIM command handler
+// Handler must be 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 ('cancel' will stop current operation)");
+		return;
+	  }
+	  // Jump to command function
+	  (this->*CommandList[i].CommandPointer)();
+	  return;  
+    }
+  }
+  
+  PrintMessage("Unknown command '%s'\n", Parameter[0].c_str());
+}
+
+//
+// Switch SRCLK
+//
+void FAD::cmd_srclk() {
+
+  for (unsigned int i=0; i<Boards.size(); i++) {
+	if (Match(Parameter[1],"on")) Boards[i]->Send(CMD_SRCLK_ON);
+	else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_SRCLK_OFF);
+  }
+
+  if (Match(Parameter[1],"on")) PrintMessage("SRCLK switched on for all active boards\n");
+  else if (Match(Parameter[1],"off")) PrintMessage("SRCLK switched off for all active boards\n");
+  else PrintUsage();
+} 
+
+//
+// Switch SCLK
+//
+void FAD::cmd_sclk() {
+
+  for (unsigned int i=0; i<Boards.size(); i++) {
+	if (Match(Parameter[1],"on")) Boards[i]->Send(CMD_SCLK_ON);
+	else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_SCLK_OFF);
+  }
+
+  if (Match(Parameter[1],"on")) PrintMessage("SCLK switched on for all active boards\n");
+  else if (Match(Parameter[1],"off")) PrintMessage("SCLK switched off for all active boards\n");
+  else PrintUsage();
+} 
+
+//
+// Switch Domino wave
+//
+void FAD::cmd_domino() {
+
+  for (unsigned int i=0; i<Boards.size(); i++) {
+	if (Match(Parameter[1],"on")) Boards[i]->Send(CMD_DENABLE);
+	else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_DDISABLE);
+  }
+  
+  if (Match(Parameter[1],"on")) PrintMessage("Domino wave switched on for all active boards\n");
+  else if (Match(Parameter[1],"off")) PrintMessage("Domino wave switched off for all active boards\n");
+  else PrintUsage();
+} 
+
+//
+// Switch DWRITE
+//
+void FAD::cmd_dwrite() {
+
+  for (unsigned int i=0; i<Boards.size(); i++) {
+	if (Match(Parameter[1],"on")) Boards[i]->Send(CMD_DWRITE_RUN);
+	else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_DWRITE_STOP);
+  }
+
+  if (Match(Parameter[1],"on")) PrintMessage("DWRITE set high for all active boards\n");
+  else if (Match(Parameter[1],"off")) PrintMessage("DWRITE set low for all active boards\n");
+  else PrintUsage();
+} 
+
+//
+// Issue soft trigger
+//
+void FAD::cmd_trigger() {
+
+  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);
+	  }
+	}
+	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 {
+	  PrintUsage();
+	  break;
+	}
+  }
+} 
+
+//
+// Set DAC
+//
+void FAD::cmd_dac() {
+
+  int Value;
+  struct Range R = {0, NDAC-1};
+  unsigned short Buffer[2*NDAC] = {0};
+
+  // Check ranges  
+  if(!ConvertToRange(Parameter[1], R)) {
+	PrintMessage("Error, DAC number out of range.\n");
+	return;
+  }
+  
+  if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_DACVAL) {
+  	PrintMessage("Error, DAC value out of range.\n");
+	return;
+  }
+  
+  // Prepare command buffer
+  for (int i=R.Min; i<=R.Max; i++) {
+	Buffer[2*i] = htons(CMD_Write | (BADDR_DAC + i));
+	Buffer[2*i+1] = htons(Value);
+  }
+
+  // Send command buffer
+  for (unsigned int i=0; i<Boards.size(); i++) {
+	Boards[i]->Send(Buffer, sizeof(Buffer));
+  }
+} 
+
+//
+// Set region-of-interest
+//
+void FAD::cmd_roi() {
+
+  int Value;
+  struct Range R = {0, NChips*NChannels-1};
+  unsigned short Buffer[2*NChips*NChannels] = {0};
+
+  // Check ranges  
+  if (!ConvertToRange(Parameter[1], R)) {
+	PrintMessage("Error, ROI number out of range.\n");
+	return;
+  }
+  
+  if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_ROIVAL) {
+  	PrintMessage("Error, ROI value out of range.\n");
+	return;
+  }
+  
+  // Prepare command buffer
+  for (int i=R.Min; i<=R.Max; i++) {
+	Buffer[2*i] = htons(CMD_Write | (BADDR_ROI + i));
+	Buffer[2*i+1] = htons(Value);
+  }
+  
+  // Send command buffer
+  for (unsigned int i=0; i<Boards.size(); i++) {
+	Boards[i]->Send(Buffer, sizeof(Buffer));
+  }
+} 
+
+//
+// Set addresses to value
+//
+void FAD::cmd_address() {
+
+  int Value;
+  struct Range R = {0, MAX_ADDR};
+  unsigned short Buffer[2*MAX_ADDR] = {0};
+
+  // Check ranges  
+  if (!ConvertToRange(Parameter[1], R)) {
+	PrintMessage("Error, address out of range.\n");
+	return;
+  }
+  
+  if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_VAL) {
+  	PrintMessage("Error, value out of range.\n");
+	return;
+  }
+  
+  // Prepare command buffer
+  for (int i=R.Min; i<=R.Max; i++) {
+	Buffer[2*i] = htons(CMD_Write | i);
+	Buffer[2*i+1] = htons(Value);
+  }
+  
+  // Send command buffer
+  for (unsigned int i=0; i<Boards.size(); i++) {
+	Boards[i]->Send(Buffer, 2*(R.Max-R.Min+1)*sizeof(unsigned short));
+  }
+} 
+
+//
+// Set ADC phase
+//
+void FAD::cmd_phase() {
+
+  int Value;
+
+  if (!ConvertToInt(Parameter[1], &Value)) {
+  	PrintMessage("Error, illegal phase value\n");
+	return;
+  }
+  
+  // Prepare command buffer
+  unsigned short *Buffer = new unsigned short [abs(Value)];
+  for (int i=0; i<abs(Value); i++) Buffer[i] = htons(CMD_PS_DO);
+  
+  // Execute phase setting
+  for (unsigned int i=0; i<Boards.size(); i++) {
+    Boards[i]->Send(CMD_PS_RESET);
+    if (Value < 0) Boards[i]->Send(CMD_PS_DIRDEC);
+	else Boards[i]->Send(CMD_PS_DIRINC);
+	Boards[i]->Send(Buffer, abs(Value)*sizeof(unsigned short));
+  }
+  
+  delete[] Buffer;
+} 
+
+//
+// Send arbitrary data to board
+//
+void FAD::cmd_send() {
+
+  int Value;
+
+  if (!ConvertToInt(Parameter[1], &Value) || Value<0 || Value>MAX_VAL) {
+  	PrintMessage("Error, illegal value\n");
+	return;
+  }
+  
+  for (unsigned int i=0; i<Boards.size(); i++) Boards[i]->Send(Value);
+} 
+
+/*
+// Set Domino mode
+void FAD::cmd_dmode() {
+  if (Match(Param[1],"continuous")) SetDOMINOMode(1);
+  else if (Match(Param[1],"single")) SetDOMINOMode(0);
+  else PrintUsage();
+} 
+
+// Set Domino readout mode
+void FAD::cmd_rmode() {
+  if (Match(Param[1],"first")) SetDOMINOReadMode(0);
+  else if (Match(Param[1],"stop")) SetDOMINOReadMode(1);
+  else PrintUsage();
+} 
+
+// Set Domino wave mode
+void FAD::cmd_wmode() {
+  if (Match(Param[1],"run")) SetDOMINOWaveMode(1);
+  else if (Match(Param[1],"stop")) SetDOMINOWaveMode(0);
+  else PrintUsage();
+} 
+*/
+
+//
+// Amplitude calibration
+//
+void FAD::cmd_acalib() {
+
+  pthread_t Thread;
+  int Code;
+
+  // Set mode before lunching thread
+  Mode = acalib;
+  Cancel = false;
+  
+  // Create detached thread
+  if ((Code = pthread_create(&Thread, NULL, (void * (*)(void *)) FAD::LaunchAmplitudeCalibration,(void *) this)) != 0) {
+    Message(ERROR, "pthread_create() failed in FAD::cmd_acalib() (%s)\n", strerror(Code));
+	Mode = idle;
+	return;
+  }
+
+  if ((Code = pthread_detach(Thread)) != 0) {
+	Message(ERROR, "pthread_detach() failed in FAD::cmd_acalib() (%s)\n", strerror(Code));
+  }
+}
+
+/*
+// Do time calibration
+void FAD::cmd_tcalib() {
+  
+  if (!IsDRSFreqSet()) {
+    PrintMessage("Set sampling frequency for all boards first\n");
+    return;
+  }
+  if (!ReadCalibration()) {
+    PrintMessage("Amplitude calibration has to be done first\n");
+    return;
+  }
+      
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    if (GetBoard(i)->GetDRSType() != 4 || GetBoard(i)->GetFirmwareVersion() < 13279) {
+      PrintMessage("Time calibration needs DRS4 and minimum firmware version 13279, skipping board %d\n", i);
+      continue;
+    }
+    PrintMessage("Creating time calibration of board %d (serial #%04d)\n  Note: No input signals should be connected\n", i, GetBoard(i)->GetBoardSerialNumber());
+    
+    GetBoard(i)->SetFrequency(DRSFreq[i], true);
+    if (GetBoard(i)->CalibrateTiming(this) != 1) {
+      PrintMessage("Time calibration method returned error status, stopping calibration\n");
+      return;
+    }
+
+    TCalib[i] = true;
+
+    // Write calibration data to file
+    float Time[NChipsMax][NBins];
+    char *Filename;
+    FILE *Calibfile;
+	bool WriteOK = true;
+	
+    // Copy calibration data into array
+	for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
+      GetBoard(i)->GetTime(Chip, Time[Chip], true, false);
+	}
+
+	// Write calibration data to file
+    if (asprintf(&Filename, "%s/TCalib_%d_%.2fGHz.txt", fCalibDataPath, GetBoard(i)->GetBoardSerialNumber(), DRSFreq[i]) == -1) {
+      PrintMessage("Error: asprintf() failed, cannot generate filename (%s)\n", strerror(errno));
+      return; 
+    }
+    if ((Calibfile=fopen(Filename,"w")) == NULL) {
+      PrintMessage("Error: Could not open file '%s' \n", Filename);      
+    }
+    else {
+      if(fprintf(Calibfile, "# DRS time calibration\n") == -1) WriteOK = false;
+
+      for (int Bin=0; Bin<NBins; Bin++) {
+		for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
+          if(fprintf(Calibfile, "%.2f ", Time[Chip][Bin]) == -1) WriteOK = false;
+		}
+        if(fprintf(Calibfile, "\n") == -1) WriteOK = false;
+	  }
+	  if (fclose(Calibfile) != 0) PrintMessage("Error closing file '%s'\n", Filename);
+	}
+	if (!WriteOK) PrintMessage("Error writing to file '%s'\n", Filename);
+	else PrintMessage("Calibration written to file '%s'\n", Filename);
+	
+    free(Filename);
+  }
+  PrintMessage("Time calibration finished\n");
+}
+*/
+
+//
+// Print status
+//
+void FAD::cmd_status() {
+
+  // ==== Print board overview ====
+  if (Parameter.size() == 1) {
+
+	// Count active board
+	unsigned int Count = 0, Error = 0;
+	for (unsigned int i=0; i<Boards.size(); i++) {
+	  if (Boards[i]->Active) Count++;
+	  if (Boards[i]->CommError) Error++;
+	}	  
+
+	PrintMessage(" Number of FAD boards: %d    Boards with communication error: %d   Active boards: ", Boards.size(), Error);
+
+	// Print list of active boards
+	if (Count == 0) PrintMessage("none\n");
+	else if (Count == Boards.size()) PrintMessage("all\n");
+	else for (unsigned int i=0; i<Boards.size(); i++) {
+	  if (Boards[i]->Active) PrintMessage(" %d", i);
+	}
+	return;
+  }    
+
+  // ==== Print details for given range ====
+  struct Range R = {0, Boards.size()};
+
+  if (!ConvertToRange(Parameter[1], R)) {
+	PrintMessage("Error, out of range.\n");
+	return;
+  }
+
+  for (int i=0; i<(int) Boards.size(); i++) {
+	if (i<R.Min || i > R.Max) continue;
+
+	struct FADBoard::BoardStatus S = Boards[i]->GetStatus();
+
+	PrintMessage("Board #%d (%sactive)    Communication %s\n", i, Boards[i]->Active ? "":"in", Boards[i]->CommError ? "ERROR":"OK");
+	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]);
+	}
+	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(),
+		    GetBoard(i)->GetTemperature(),
+		    ACalibTemp[i]);
+
+
+	if (GetBoard(i)->GetStatusReg() & BIT_RUNNING)
+	  PrintMessage("   Domino wave running\n");
+
+	if (GetBoard(i)->GetCtrlReg() & BIT_DMODE)
+	  PrintMessage("   DMODE circular\n");
+	else
+	  PrintMessage("   DMODE single shot\n");
+	if (GetBoard(i)->GetCtrlReg() & BIT_ENABLE_TRIGGER1)
+	  PrintMessage("   ENABLE_TRIGGER\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()
+}
+
+
+//
+// Adress FAD boards
+//
+void FAD::cmd_board() {
+  
+  struct Range R = {0, Boards.size()};
+  int Mode = 0;
+  
+  // Check if given boards should be enabled or disabled
+  if (Parameter[1].size() >= 1) {
+    if (Parameter[1][0] == '+') Mode = 1;
+    if (Parameter[1][0] == '-') Mode = -1;
+  }
+  if (Mode != 0) Parameter[1][0] = ' ';
+
+  // Evaluate given range
+  if (!ConvertToRange(Parameter[1], R)) {
+	PrintMessage("Error, out of range.\n");
+	return;
+  }
+  
+  // Enable or disable boards
+  for (int i=0; i<(int) Boards.size(); i++) {
+	if (Mode == 0) Boards[i]->Active = false;
+	if (i >= R.Min && i <= R.Max) {
+	  if (Mode != -1) Boards[i]->Active = true;
+	  else Boards[i]->Active = false;
+	}
+  }  
+} 
+
+//
+// Set DIM event update delay
+//
+void FAD::cmd_update() {
+
+  double Delay;
+  
+  if (Parameter.size()==2 && ConvertToDouble(Parameter[1], &Delay) && Delay>0) EventUpdateDelay = Delay;
+  else PrintUsage();
+}
+
+//
+// Print help
+//
+void FAD::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"
+   "Items in <> are mandatory, in [] optional, | indicates mutual exclusive.\n"
+   "Strings containing spaces have to be enclosed in \"double quotes\".\n"
+   "Ranges can be given as 'all', a single number or in the form 'a-b'.\n"); 
+}
+
+//
+// Cancel current operation
+//
+void FAD::cmd_cancel() {
+
+  if (Mode == idle) PrintMessage("Nothing to cancel\n");
+  else {
+    PrintMessage("Requested cancelation of current operation\n");
+    Cancel = true;
+  }
+}
+
+//
+// Exit programm
+// SIGTERM sets ExitRequest flag, and also makes readline() return (if command from DimCommand thread)
+//
+void FAD::cmd_exit() {
+
+  pthread_kill(MainThread, SIGTERM);
+}
+
+
+// -----------------------------
+// *****  Other functions  *****
+// -----------------------------
+
+//
+// Amplitude calibration (lauched as thread by cmd_acalib())
+//
+void FAD::AmplitudeCalibration() {
+
+  static unsigned int ReqNum = 10;
+  unsigned short Buffer[2*NChips*NChannels];
+  unsigned short DACCmd[] = {htons(CMD_Write | (BADDR_DAC + 0)), 0};
+
+  /* Procedure
+
+  1. Register sampling frequency from FTM!
+  ...
+  5. Issue single trigger and verify settings
+  ...
+  11. Secondary calibration
+*/
+  struct FADBoard::BoardStatus *Status = new struct FADBoard::BoardStatus [Boards.size()];
+
+  PrintMessage("Staring amplitude calibration of all active boards/n  Note: No input signals must be connected\n");
+  
+  // Initialise settings for calibration
+  for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
+
+	// Save initial board status
+	Status[Brd] = Boards[Brd]->GetStatus();
+	
+    // Set all ROI to 1024
+	for (unsigned int i=0; i<NChips*NChannels; i++) {
+	  Buffer[2*i] = htons(CMD_Write | (BADDR_ROI + i));
+	  Buffer[2*i+1] = htons(NBins);
+	}
+	Boards[Brd]->Send(Buffer, sizeof(Buffer));
+
+    // Set DAC first value
+    DACCmd[1] = htons(10);
+	Boards[Brd]->Send(DACCmd, sizeof(DACCmd));
+
+	// Start accumulation
+    Boards[Brd]->AccumulateSum(ReqNum);	
+	Boards[Brd]->Send(CMD_Trigger_C);
+  }
+
+  // Wait until data for all boards taken
+  bool Done = false;
+  while (!Done && !Cancel) {
+    usleep(300000);
+	for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
+	  Done = true;
+	  if (Boards[Brd]->Active && Boards[Brd]->DoSum) Done = false;
+	}
+  }
+
+  for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
+	// Determine baseline
+	for (unsigned int i=0; i<NChips; i++) for (unsigned int j=0; j<NChannels; j++) {
+	  for (unsigned int k=0; k<NBins; k++) Boards[Brd]->Baseline[i][j][k] = Boards[Brd]->Sum[i][j][k] / 10;
+	}
+	// Set second DAC value
+    DACCmd[1] = htons(30000);
+	Boards[Brd]->Send(DACCmd, sizeof(DACCmd));
+	// Start accumulation
+    Boards[Brd]->AccumulateSum(ReqNum);	
+  }
+
+  // Wait until data for all boards taken
+  Done = false;
+  while (!Done && !Cancel) {
+    usleep(300000);
+	for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
+	  Done = true;
+	  if (Boards[Brd]->Active && Boards[Brd]->DoSum) Done = false;
+	}
+  }
+
+  // Stop triggering, write back original ROI and DAC settings
+  for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
+  	Boards[Brd]->Send(CMD_Trigger_S);
+
+	// Determine gain
+	for (unsigned int i=0; i<NChips; i++) for (unsigned int j=0; j<NChannels; j++) {
+	  for (unsigned int k=0; k<NBins; k++) Boards[Brd]->Gain[i][j][k] = (Boards[Brd]->Sum[i][j][k] / 10)/Boards[Brd]->Baseline[i][j][k];
+	}
+
+	for (unsigned int i=0; i<NChips*NChannels; i++) {
+	  Buffer[2*i] = htons(CMD_Write | (BADDR_ROI + i));
+	  Buffer[2*i+1] = htons(Status[Brd].ROI[i/NChannels][i%NChannels]);
+	}
+	Boards[Brd]->Send(Buffer, sizeof(Buffer));
+  
+    DACCmd[1] = htons(Status[Brd].DAC[0]);
+  	Boards[Brd]->Send(DACCmd, sizeof(DACCmd));
+	
+	// Store calibration time and temperature
+	Boards[Brd]->ACalibTime = Cancel ? -1 : time(NULL);
+	Boards[Brd]->ACalibTemp = 0;
+	for (unsigned int i=0; i<NTemp; i++) Boards[Brd]->ACalibTemp += Status[Brd].Temp[i] / NTemp;
+  }
+
+  delete[] Status;
+  
+  PrintMessage("Amplitude calibration of all active boards finished, original ROI and DAC set\n");
+  Mode = idle;
+  
+  // Write short calibration information
+  /*time_t Time = time(NULL);
+  FILE *InfoFile = fopen(CalibInfoFilename, "w");
+  if (InfoFile != NULL) {
+	fprintf(InfoFile, "# Calibration information as of %s\n", ctime(&Time));
+	for (int i=0; i<GetNumberOfBoards(); i++) {
+	  fprintf(InfoFile, "%d %d %.1f %d %.2f\n", GetBoard(i)->GetBoardSerialNumber(), ACalib[i], ACalibTemp[i], TCalib[i], DRSFreq[i]);
+	}
+	fclose(InfoFile);
+  }
+  else PrintMessage("Could not write calibration information to file '%s'\n", CalibInfoFilename);
+*/
+}
+
+// Launch read thread inside class
+void FAD::LaunchAmplitudeCalibration(class FAD *m) {
+
+  m->AmplitudeCalibration();
+}
+
+
+//
+// DIM event service update thread (publishes M0 format)
+//
+void FAD::EventThread() {
+
+  struct timeval Time;
+  struct timeval LastUpdate; 
+  bool Update;
+  struct FADBoard::BoardStatus S;
+
+  gettimeofday(&LastUpdate, NULL);
+
+  // Create DIM event data service
+  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);
+
+  memset(EventData, 0, EventSize);
+
+  // Calculate pointers to EventData array
+  RunHeader *RHeader = (RunHeader *) EventData;
+  BoardStructure **BStruct = new BoardStructure * [Boards.size()];
+  for (unsigned int i=0; i<Boards.size(); i++) BStruct[i] = ((BoardStructure *) (RHeader + 1)) + i;
+  EventHeader *EHeader = (EventHeader *) ((char *) (RHeader + 1) + Boards.size()*sizeof(BoardStructure));
+  int *TriggerCell = (int *) (EHeader + 1);
+  short *Data = (short *) (TriggerCell + NChips*Boards.size());
+
+  // M0 RunHeader
+  RHeader->MagicNum = 0xE0E0;
+  RHeader->DataFormat = 1;
+  RHeader->RunHeaderSize = sizeof(RunHeader);
+  RHeader->EventHeaderSize = sizeof(EventHeader);
+  RHeader->BoardStructureSize = sizeof(BoardStructure);
+  RHeader->SoftwareRevision = 0xFFFF;	// Update
+  RHeader->Identification = 0;
+
+  RHeader->Type = 0;				// Run type: 0=data, 1=pedestal, 3=test
+  RHeader->Events = 1;
+
+  RHeader->RunNumber = -1;
+  RHeader->FileNumber = 0;
+  snprintf(RHeader->Description, sizeof(RHeader->Description), "FADctrl_Event");       
+
+  RHeader->NBoards = Boards.size();
+  RHeader->NChips = NChips;
+  RHeader->NChannels = NChannels;
+  RHeader->Samples = NBins; 			// Always full pipeline
+  RHeader->Offset = 0;
+  RHeader->NBytes = 2;
+
+  // M0 EventHeader
+  EHeader->EventSize = Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int));
+
+  // Update loop
+  while (!ExitRequest) {
+    usleep(EventUpdateDelay);
+
+	// Update run and event header with current time
+	gettimeofday(&Time, NULL);	
+	RHeader->StartSecond = Time.tv_sec;
+	RHeader->StartMicrosecond = Time.tv_usec;		
+	RHeader->EndSecond = Time.tv_sec;			
+	RHeader->EndMicrosecond = Time.tv_usec;		
+
+	EHeader->Second = Time.tv_sec;
+	EHeader->Microsecond = Time.tv_usec;
+
+	// Check boards for new data since last update
+	Update = false;
+	for (unsigned int Brd=0; Brd<Boards.size(); Brd++) {
+	  S = Boards[Brd]->GetStatus();
+	  if (S.Update.tv_sec>LastUpdate.tv_sec || ((S.Update.tv_sec==LastUpdate.tv_sec) && (S.Update.tv_sec>LastUpdate.tv_sec))) {
+
+		Update = true;
+
+		// Fill M0 BoardStructure		
+		BStruct[Brd]->SerialNo = S.BoardID;
+		BStruct[Brd]->NomFreq = 2;
+		BStruct[Brd]->BoardTemp = 0;        
+		for (unsigned int i=0; i<NTemp; i++) BStruct[Brd]->BoardTemp += S.Temp[i]/NTemp;
+		BStruct[Brd]->ScaleFactor = 1/2.048;     
+
+		// Update event header with ID and Type of current board
+		EHeader->EventNumber = S.TriggerID;
+		EHeader->TriggerType = S.TriggerType;
+
+		// Write trigger cells
+		for(unsigned int i=0; i<NChips; i++) TriggerCell[Brd*NChips+i] = (int) S.TriggerCell[i];
+
+		// Write channel data (stored in 12 bit signed twis complement with out-of-range-bit and leading zeroes)
+		int Count = 0;
+		memset(Data, 0, Boards.size()*NChips*NChannels*NBins*sizeof(short));
+
+		Boards[Brd]->Lock();
+		for (unsigned int Chip=0; Chip<NChips; Chip++) for (unsigned int Chan=0; Chan<NChannels; Chan++) {
+		  for (int i=0; i<S.ROI[Chip][Chan]; i++) Data[Count++] = Boards[Brd]->Data[Chip][Chan][i];
+		  Count += NBins - S.ROI[Chip][Chan];
+		}
+		Boards[Brd]->Unlock();
+	  }
+	}
+
+	if (Update) {
+	  gettimeofday(&LastUpdate, NULL);
+	  EventService->updateService(EventData, EventSize);
+	}
+  }
+
+  // Clean up
+  delete[] BStruct;
+  delete EventService;
+  delete[] EventData;
+}
+
+// Launch event thread inside class
+void FAD::LaunchEventThread(class FAD *m) {
+
+  m->EventThread();
+}
+
+/*
+// Read calibration data
+bool FAD::ReadCalibration() {
+
+  static char Buffer[MAX_COM_SIZE];
+  int Serial, Calib;
+  float Temp, Freq;
+
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    if (GetBoard(i)->GetDRSType() == 4) {
+      if (ACalib[i] == false) {
+	    // Check calibration info file if EEPROM data on DRS board still valild
+		FILE *CalibInfo = fopen(CalibInfoFilename, "r");
+		if (CalibInfo == NULL) return false;
+		fgets(Buffer, sizeof(Buffer), CalibInfo); // skip first two lines
+		fgets(Buffer, sizeof(Buffer), CalibInfo);
+
+		while (fgets(Buffer, sizeof(Buffer), CalibInfo) != NULL) {
+		  if (sscanf(Buffer, "%d %d %f %*d %f", &Serial, &Calib, &Temp, &Freq) != 4) {
+			fclose(CalibInfo);
+			return false;
+		  }
+
+		  if (Serial==GetBoard(i)->GetBoardSerialNumber() && int(Freq*100)==int(DRSFreq[i]*100) && Calib==1) {
+			ACalib[i] = true;
+			ACalibTemp[i] = Temp;
+			break;
+		  }
+		}
+		fclose(CalibInfo);
+	  }
+    }
+    else {
+      if (!ACalib[i]) {
+        GetBoard(i)->SetCalibrationDirectory(fCalibDataPath);
+        PrintMessage("Reading response calibration file for board %d from: \"%s\"\n", i, fCalibDataPath);
+        for (int Chip=0; Chip<GetBoard(i)->GetNumberOfChips(); Chip++) {
+          if (GetBoard(i)->GetResponseCalibration()->ReadCalibration(Chip) == false) return false;
+        }
+        ACalib[i] = true;
+      }
+    }
+	if (fabs(ACalibTemp[i]-GetBoard(i)->GetTemperature())>2) PrintMessage("Warning: Large difference to calibration temperature for board %d\n", i);
+  } // Loop over boards
+  return true;
+}
+
+// Set DOMINO mode 
+void FAD::SetDOMINOMode(int mode) {
+ 
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    GetBoard(i)->SetDominoMode(mode==1 ? 1:0);
+    PrintMessage("Domino mode of board %d switched to %s.\n",i,mode==1 ? "continuous":"single shot");
+  } 
+}
+
+// Set DOMINO readout mode 
+void FAD::SetDOMINOReadMode(int mode) {
+
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    GetBoard(i)->SetReadoutMode(mode);
+    PrintMessage("Start readout of board %d from %s.\n",i,mode==0 ? "first bin":"stop position");
+  } 
+}
+
+// Set DOMINO wave mode 
+void FAD::SetDOMINOWaveMode(int mode) {
+
+  for (int i=FirstBoard; i<=LastBoard; i++) {
+    GetBoard(i)->SetDominoActive(mode);
+    PrintMessage("Domino wave of board %d is %s during readout\n",i,mode==1 ? "running":"stopped");
+  } 
+}
+
+*/
+
+
+//
+// Print usage text for command
+//
+void FAD::PrintUsage() {
+
+  for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
+    if (Match(Parameter[0], CommandList[i].Name)) {
+	  PrintMessage("Usage: %s %s\n", CommandList[i].Name, CommandList[i].Parameters);
+	}
+  }
+}
+
+//
+// Print message to console
+//
+void FAD::PrintMessage(const char *Format, ...) {
+
+  static char Error[] = "vasprintf() failed in PrintMessage()";
+  char *Text;
+
+  // Evaluate arguments
+  va_list ArgumentPointer;
+  va_start(ArgumentPointer, Format);
+  if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
+  va_end(ArgumentPointer);
+  
+  // Print to console
+  printf("%s", Text);
+  fflush(stdout);
+  if (strlen(Text)>0 && Text[strlen(Text)-1]=='\n') rl_on_new_line(); // New prompt
+
+  // Send to DIM text service
+  ConsoleOut->updateService(Text); 
+
+  // Free old text
+  if (ConsoleText != Error) free(ConsoleText);
+  ConsoleText = Text; 
+}
+
+//
+// Check if two strings match (min 1 character must match)
+//
+bool FAD::Match(string str, const char *cmd) {
+
+  return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true;
+}
+
+//
+// Conversion function from string to double, int or range
+//
+// Return false if conversion did not stop on whitespace or EOL character
+bool FAD::ConvertToDouble(string String, double *Result) {
+
+  char *EndPointer;
+  
+  *Result = strtod(String.c_str(), &EndPointer);
+  if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
+  return true;
+}
+
+bool FAD::ConvertToInt(string String, int *Result) {
+
+  char *EndPointer;
+  
+  *Result = (int) strtol(String.c_str(), &EndPointer, 0);
+  if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
+  return true;
+}
+
+bool FAD::ConvertToRange(string String, struct FAD::Range &R) {
+
+  int N, M;
+
+  // Full range
+  if (Match(String, "all")) return true;
+
+  // Single number
+  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], &N) && ConvertToInt(V[1], &M) && N>=R.Min && M<=R.Max) {
+	R.Min = N;
+	R.Max = M;
+	return true;
+  }
+  
+  return false;
+}
Index: /fact/FADctrl/FAD.h
===================================================================
--- /fact/FADctrl/FAD.h	(revision 10036)
+++ /fact/FADctrl/FAD.h	(revision 10036)
@@ -0,0 +1,88 @@
+#ifndef FAD_H_SEEN
+#define FAD_H_SEEN
+
+//#define SERVER_NAME "FADctrl"       // Name to use in DIM
+#define SERVER_NAME "drsdaq"       // Name to use in DIM
+#include "Evidence.h"
+
+#include <readline/readline.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <limits>
+
+#include "FADBoard.h"
+
+#define MAX_COM_SIZE 10000
+
+enum ModeType {idle, acalib, tcalib};
+
+class FAD: public EvidenceServer {
+
+	pthread_t MainThread;
+	DimCommand *Command;
+	DimService *ConsoleOut;
+	char *ConsoleText;
+	std::vector<std::string> Parameter;
+	ModeType Mode;
+	volatile bool Cancel;
+    void PrintUsage();
+	void commandHandler();
+	bool Match(std::string, const char *);
+	void AmplitudeCalibration();
+	static void LaunchAmplitudeCalibration(class FAD *);
+	void EventThread();
+	static void LaunchEventThread(class FAD *);
+	pthread_t Thread;
+	int EventUpdateDelay;
+
+    //const char *fCalibDataPath;
+    //char CalibInfoFilename[MAX_PATH];
+	
+	std::vector<std::string> BoardList;
+	std::vector<class FADBoard *> Boards;
+
+	struct Range {
+	  int Min;
+	  int Max;
+	};
+
+	bool ConvertToDouble(std::string, double *);
+	bool ConvertToInt(std::string, int *);
+	bool ConvertToRange(std::string, struct Range &);
+
+  public:
+    FAD();
+    ~FAD();
+
+    void cmd_exit();		void cmd_help();
+    void cmd_board();		void cmd_status();
+    void cmd_acalib();		void cmd_serial();
+    void cmd_trigger();
+	void cmd_srclk();		void cmd_sclk();
+	void cmd_dwrite();		void cmd_domino();
+    void cmd_wmode();		void cmd_rmode();
+    void cmd_dmode();
+	void cmd_tcalib();		void cmd_dac();
+	void cmd_roi();			void cmd_address();
+	void cmd_phase();		void cmd_send();
+	void cmd_cancel();		void cmd_update();
+
+    void EnableDomino();
+    void DisableDomino();
+    void SoftTrigger();
+    void SetDOMINOMode(int);
+    void SetDOMINOReadMode(int);
+    void SetDOMINOWaveMode(int);
+    bool ReadCalibration();
+    void PrintMessage(const char*, ...);
+};
+
+#endif
Index: /fact/FADctrl/FADBoard.cc
===================================================================
--- /fact/FADctrl/FADBoard.cc	(revision 10036)
+++ /fact/FADctrl/FADBoard.cc	(revision 10036)
@@ -0,0 +1,339 @@
+/********************************************************************\
+
+  Class interfacing to FAD board
+
+\********************************************************************/
+
+#include "FADBoard.h"
+using namespace std;
+
+//
+// Constructor
+// 
+FADBoard::FADBoard(string Server, unsigned short Port, class FAD *Parent, unsigned int Num) {
+
+  int Ret;
+
+  // Initialization
+  m = Parent;
+  InitOK = false;
+  Active = true;
+  CommError = false;
+  ACalibTime = -1;
+  Status.Update.tv_sec = -1;
+  Thread = pthread_self(); // For checking in destructor
+
+  Name = new char [Server.size()+1]; // Name in permanent memory for DIM service
+  strcpy(Name, Server.c_str());
+
+  // Resolve hostname
+  struct hostent *Host = gethostbyname(Server.c_str());
+  if (Host == 0) {
+    m->PrintMessage("Could not resolve host name for %s\n", Server.c_str());
+    return;
+  }
+
+  // Open socket descriptor
+  if ((Socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+    m->PrintMessage("Could not open socket for %s (%s)\n", Server.c_str(), strerror(errno));
+    return;
+  }
+    
+  // Connect to server
+  struct sockaddr_in SocketAddress;
+  SocketAddress.sin_family = PF_INET;
+  SocketAddress.sin_port = htons(Port);
+  SocketAddress.sin_addr = *(struct in_addr*) Host->h_addr;
+  
+  if (connect(Socket, (struct sockaddr *) &SocketAddress, sizeof(SocketAddress)) == -1) {
+    m->PrintMessage("Could not connect to %s at port %d (%s)\n", Server.c_str(), Port, strerror(errno));
+    return;
+  }
+  
+  // Construct DIM service name prefix
+  stringstream ID;
+  ID << SERVER_NAME"/Board" << setfill('0') << setw(2) << Num << "/";
+
+  NameService = new DimService((ID.str()+"Server").c_str(), Name);
+  IDService = new DimService((ID.str()+"BoardID").c_str(), (char *) "S", NULL, 0);
+  TempService = new DimService((ID.str()+"Temperature").c_str(), (char *) "F", NULL, 0);
+  DACService = new DimService((ID.str()+"DAC").c_str(), (char *) "S", NULL, 0);
+  ROIService = new DimService((ID.str()+"ROI").c_str(), (char *) "S", NULL, 0);
+
+  // Initialise mutex for synchronization
+  pthread_mutexattr_t Attr;
+
+  if ((Ret = pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
+    m->Message(m->ERROR, "pthread_mutex_settype() failed (%s)", strerror(Ret));
+  }
+  if ((Ret = pthread_mutex_init(&Mutex, &Attr)) != 0) {
+    m->Message(m->ERROR, "pthread_mutex_init() failed (%s)", strerror(Ret));
+	return;
+  }
+
+  // Create thread that receives data
+  if ((Ret = pthread_create(&Thread, NULL, (void * (*)(void *)) LaunchThread,(void *) this)) != 0) {
+    m->PrintMessage("pthread_create() failed (%s)\n", strerror(Ret));
+	Thread = pthread_self();
+	return;
+  }
+
+  InitOK = true;
+}
+
+//
+// Destructor
+//
+FADBoard::~FADBoard() {
+
+  int Ret;
+
+  // Cancel thread and wait for it to quit
+  if (pthread_equal(Thread, pthread_self()) == 0) {
+	if ((Ret = pthread_cancel(Thread)) != 0) m->PrintMessage("Error: Could not request thread cancellation (%s)\n", strerror(Ret));
+	if ((Ret = pthread_join(Thread, NULL)) != 0) m->PrintMessage("pthread_join() failed (%s)\n", strerror(Ret));
+  }
+
+  // Delete mutex  
+  if (InitOK && ((Ret = pthread_mutex_destroy(&Mutex)) != 0)) {
+	m->PrintMessage("pthread_mutex_destroy() failed (%s)", strerror(Ret));
+  }
+
+  delete NameService;
+  delete IDService;
+  delete TempService;
+  delete DACService;
+  delete ROIService;
+  delete[] Name;
+
+  // Close socket descriptor
+  if ((Socket != -1) && (close(Socket) == -1)) {
+	m->PrintMessage("Could not close socket descriptor (%s)", strerror(errno));  
+  }
+}
+
+
+//
+// Send data to board
+//
+void FADBoard::Send(const void *Data, size_t Bytes) {
+
+  // Do not send if not active
+  if (!Active) return;
+
+  // Write data
+  ssize_t Result = write(Socket, Data, Bytes);
+
+  // Check result
+  if (Result == -1) m->PrintMessage("Error: Could not write to socket (%s)\n", strerror(errno));
+  else if ((size_t) Result < Bytes) m->PrintMessage("Error: Could only write %d bytes out of %d to socket\n", Result, Bytes);
+}
+
+void FADBoard::Send(unsigned short Data) {
+
+  unsigned short Buffer = htons(Data);
+  
+  Send(&Buffer, sizeof(unsigned short));
+}
+
+
+//
+// Get board status (mutex protected to avoid concurrent access in ReadLoop)
+//
+struct FADBoard::BoardStatus FADBoard::GetStatus() {
+
+  int Ret;
+  struct BoardStatus S;
+  
+  // Lock
+  if ((Ret = pthread_mutex_lock(&Mutex)) != 0) {
+	m->Message(m->FATAL, "pthread_mutex_lock() failed in ReadLoop() (%s)", strerror(Ret));
+  }
+
+  S = Status; 
+
+  // Unlock
+  if ((Ret = pthread_mutex_unlock(&Mutex)) != 0) {
+	m->Message(m->FATAL, "pthread_mutex_unlock() failed in Unlock() (%s)", strerror(Ret));
+  }
+  
+  return S;  
+}
+
+//
+// Initiate average
+//
+void FADBoard::AccumulateSum(unsigned int n) {
+
+  if (!Active) return;
+  
+  Lock();
+  memset(Sum, 0, sizeof(Sum));
+  NumForSum = n;
+  DoSum = true; 
+  Unlock();
+}
+
+
+//
+// Read data from board
+//
+void FADBoard::ReadLoop() {
+
+  static char Buffer[READ_BUFFER_SIZE];
+  static unsigned int Pos = 0;
+  const PEVNT_HEADER *Header = (PEVNT_HEADER *) Buffer;
+  ssize_t Result;
+  struct BoardStatus PrevStatus;
+
+  memset(&PrevStatus, 0, sizeof(PrevStatus));
+
+  while (!m->ExitRequest) {
+    // Read data from socket
+    Result = read(Socket, Buffer + Pos, sizeof(Buffer)-Pos);
+
+	// Check result of read
+	if (Result == -1) {
+	  m->PrintMessage("Error: Could not read from socket, exiting read loop (%s)\n", strerror(errno));
+	  CommError = true;
+	  break;
+	}
+	else if (Result == 0) {
+	  m->PrintMessage("Server not existing anymore, exiting read loop\n");
+	  CommError = true;
+	  break;
+	}
+	
+	// If not active, discard incoming data
+	if (!Active) continue;
+	
+	// Advance write pointer
+	Pos += Result;
+	
+	// Check if internal buffer full
+	if (Pos == sizeof(Buffer)) {
+	  m->PrintMessage("Internal buffer full, deleting all data in buffer\n");
+	  Pos = 0;
+	  continue;
+	}
+	
+	// Check if full event available in buffer
+	if (Pos < sizeof(PEVNT_HEADER) || ntohs(Header->start_package_flag) != 0xfb01) continue;
+	
+	unsigned int Length = ntohs(Header->package_length)*2*sizeof(char);
+	if (Pos < Length) continue;
+
+	// Extract data if event end package flag correct
+	if (ntohs(*(unsigned short *) (Buffer+Length-sizeof(unsigned short))) == 0x04FE) {
+
+	  // Prepare pointers to channel data (channels stored in order 0,9,18,27 - 1,10,19,28 - ... - 8,17,26,35)
+	  PCHANNEL *Channel[NChips*NChannels], *Pnt=(PCHANNEL *) (Header+1); 
+	  for(unsigned int i=0; i<NChips*NChannels; i++) {
+		Channel[i] = Pnt;
+		Pnt = (PCHANNEL *) ((short *) (Channel[i] + 1) + ntohs(Channel[i]->roi));
+	  } 
+
+	  PrevStatus = Status;
+
+	  // Lock to avoid concurrent access in GetStatus()
+	  Lock();
+
+	  gettimeofday(&Status.Update, NULL);
+
+	  // Extract ID and type information
+	  Status.BoardID = ntohl(Header->board_id);
+	  Status.TriggerID = ntohl(Header->local_trigger_id);
+	  Status.TriggerType = ntohs(Header->local_trigger_type);
+
+	  // Extract temperatures (MSB indicates if temperature is positive or negative)
+	  for (unsigned int i=0; i<NTemp; i++) {
+		if ((ntohs(Header->drs_temperature[i]) & 0x8000) == 0) Status.Temp[i] = float(ntohs(Header->drs_temperature[i]) >> 3)/16;
+		else Status.Temp[i] = float(0xE000 | (ntohs(Header->drs_temperature[i])) >> 3)/16;
+	  }
+
+	  // Extract DAC channels
+	  for (unsigned int i=0; i<NDAC; i++) Status.DAC[i] = ntohs(Header->dac[i]);
+
+	  short Buf;
+	  for (unsigned int Chip=0; Chip<NChips; Chip++) {
+		// Extract trigger cells	  
+		Status.TriggerCell[Chip] = (int) ntohs(Channel[Chip]->start_cell);
+	  
+		for (unsigned int Chan=0; Chan<NChannels; Chan++) {
+		  // Extract ROI
+		  Status.ROI[Chip][Chan] = ntohs(Channel[Chip+NChips*Chan]->roi);
+
+		  // Extract ADC data (stored in 12 bit signed twis complement with out-of-range-bit and leading zeroes)
+		  for (int i=0; i<Status.ROI[Chip][Chan]; i++) {
+			  Buf = ntohs(Channel[Chip+NChips*Chan]->adc_data[i]);
+			  (Buf <<= 4) >>= 4;			//delete the sign-bit by shifting left and shift back
+			  Data[Chip][Chan][i] = Buf;					
+		  }
+		}
+	  }
+	  
+	  // If requested for calibration, add rotated data for later averaging
+	  if (DoSum && NumForSum>0) {
+		for (unsigned int Chip=0; Chip<NChips; Chip++) for (unsigned int Chan=0; Chan<NChannels; Chan++) {
+		  for (int i=0; i<Status.ROI[Chip][Chan]; i++) {
+			Sum[Chip][Chan][(i+ntohs(Channel[Chip+NChips*Chan]->start_cell)) % NBins] = Data[Chip][Chan][i];
+		  }
+		}
+		NumForSum--;
+		if (NumForSum == 0) DoSum = false;
+	  }
+
+	  Unlock();
+	  
+	  // Update DIM services if necessary
+	  if (memcmp(PrevStatus.Temp, Status.Temp, sizeof(Status.Temp)) != 0) {
+		TempService->updateService(Status.Temp, sizeof(Status.Temp));
+	  }
+	  if (memcmp(PrevStatus.DAC, Status.DAC, sizeof(Status.DAC)) != 0) {
+		DACService->updateService(Status.DAC, sizeof(Status.DAC));
+	  }  
+	  if (memcmp(PrevStatus.ROI, Status.ROI, sizeof(Status.ROI)) != 0) {
+		ROIService->updateService(Status.ROI, sizeof(Status.ROI));
+	  }  
+	  if (PrevStatus.BoardID != Status.BoardID) {
+		IDService->updateService(&Status.BoardID, sizeof(Status.BoardID));
+	  }  
+	}
+	else printf("End package flag incorrect, removing corrupt event\n");
+
+	// Remove event data from internal buffer
+	memmove(Buffer, Buffer+Length, Pos-Length);
+	Pos = Pos-Length;	
+  } // while()
+}
+
+
+//
+// Launch read thread inside class
+//
+void FADBoard::LaunchThread(class FADBoard *m) {
+
+  m->ReadLoop();
+}
+
+
+//
+// Lock and unlock mutex
+//
+void FADBoard::Lock() {
+
+  int Ret;
+
+  if ((Ret = pthread_mutex_lock(&Mutex)) != 0) {
+	m->Message(m->FATAL, "pthread_mutex_lock() failed in class FADBoard (%s)", strerror(Ret));
+  }
+}
+
+void FADBoard::Unlock() {
+
+  int Ret;
+
+  if ((Ret = pthread_mutex_unlock(&Mutex)) != 0) {
+	m->Message(m->FATAL, "pthread_mutex_unlock() failed in class FADBoard (%s)", strerror(Ret));
+  }
+}
Index: /fact/FADctrl/FADBoard.h
===================================================================
--- /fact/FADctrl/FADBoard.h	(revision 10036)
+++ /fact/FADctrl/FADBoard.h	(revision 10036)
@@ -0,0 +1,76 @@
+#ifndef FADBoard_H_SEEN
+#define FADBoard_H_SEEN
+
+#include <ctype.h>
+#include <time.h>
+#include <math.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/time.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <iostream>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "FAD.h"
+#include "FADFormat.h"
+
+const unsigned int READ_BUFFER_SIZE = 1000000;
+
+class FADBoard {
+
+	class FAD *m;
+	int Socket;
+	char *Name;
+	pthread_t Thread;
+	pthread_mutex_t Mutex;
+	DimService *NameService;
+	DimService *IDService;
+	DimService *TempService;
+	DimService *ROIService;
+	DimService *DACService;
+
+	void ReadLoop();
+	static void LaunchThread(class FADBoard *);
+
+  public: 
+    FADBoard(std::string, unsigned short, class FAD *, unsigned int);
+    ~FADBoard();
+
+	struct BoardStatus {
+	  unsigned short BoardID;
+	  unsigned long TriggerID;
+	  unsigned char TriggerType;
+	  unsigned short TriggerCell[NChips];
+	  unsigned short ROI[NChips][NChannels];
+	  unsigned short DAC[NDAC];
+	  float Temp[NTemp];
+	  struct timeval Update;
+	} Status;
+
+	unsigned short Data[NChips][NChannels][NBins];
+	double Baseline[NChips][NChannels][NBins];
+	double Gain[NChips][NChannels][NBins];
+	float ACalibTemp;
+	time_t ACalibTime;
+
+	double Sum[NChips][NChannels][NBins];
+	unsigned int NumForSum;
+	bool DoSum;
+
+	void Send(const void *, size_t);
+	void Send(unsigned short);
+	struct BoardStatus GetStatus();
+	void AccumulateSum(unsigned int);
+	void Lock();
+	void Unlock();
+
+	bool InitOK;
+	bool CommError;
+	bool Active;
+};
+
+#endif
Index: /fact/FADctrl/FADFormat.h
===================================================================
--- /fact/FADctrl/FADFormat.h	(revision 10036)
+++ /fact/FADctrl/FADFormat.h	(revision 10036)
@@ -0,0 +1,75 @@
+#ifndef FADFORMAT_H_SEEN
+#define FADFORMAT_H_SEEN
+
+#include "../drsdaq/RawDataCTX.h"
+
+//
+// Commands for FAD
+//
+#define CMD_Start 0xC000		// Start Run
+#define CMD_Stop 0x3000			// Stop Run
+#define CMD_Trigger 0xA000 		// single trigger
+#define CMD_DENABLE 0x0600      // DENABLE line HIGH
+#define CMD_DDISABLE 0x0700     // DENABLE line LOW
+#define CMD_DWRITE_RUN 0x0800   // DWRITE possibly HIGH
+#define CMD_DWRITE_STOP 0x0900  // DWRITE always LOW
+#define CMD_SCLK_ON 0x1000  
+#define CMD_SCLK_OFF 0x1100
+#define CMD_PS_DIRINC 0x1200
+#define CMD_PS_DIRDEC 0x1300
+#define CMD_PS_DO 0x1400
+#define CMD_PS_RESET 0x1700
+#define CMD_SRCLK_ON 0x1500
+#define CMD_SRCLK_OFF 0x1600
+#define CMD_Trigger_C 0xB000 	// continous trigger
+#define CMD_Trigger_S 0x2000 	// stop continous trigger
+#define CMD_Read 0x0A00 		// read from Config-RAM
+#define CMD_Write 0x0500 		// write to Config-RAM
+
+//
+// Other definitions
+//
+#define MAX_ADDR 0xFF // highest address in Config-RAM
+#define BADDR_ROI 0x00 // Baseaddress ROI-Values
+#define BADDR_DAC 0x24 // Baseaddress DAC-Values
+#define MAX_VAL 65535
+#define MAX_ROIVAL 1024
+#define MAX_DACVAL 65535
+
+const unsigned int NChannels	= 9;
+const unsigned int NBins		= 1024;
+const unsigned int NChips 		= 4;
+const unsigned int NTemp		= 4;
+const unsigned int NDAC			= 8;
+
+//
+// Data structures
+//
+typedef struct {
+	unsigned short start_package_flag;
+	unsigned short package_length;
+	unsigned short version_no;
+	unsigned long trigger_id;
+	unsigned char trigger_type;
+	unsigned char trigger_crc;
+	unsigned long local_trigger_id;
+	unsigned char local_trigger_type;
+	unsigned char local_trigger_crc;
+	unsigned short board_id;
+	short drs_temperature[NTemp];
+	unsigned short dac[NDAC];
+}  __attribute__((__packed__)) PEVNT_HEADER;
+
+typedef struct {
+	unsigned short id;
+	unsigned short start_cell;
+	unsigned short roi;
+	unsigned short adc_data[];
+}  __attribute__((__packed__)) PCHANNEL;
+
+typedef struct {
+	unsigned short package_crc;
+	unsigned short end_package_flag;
+}  __attribute__((__packed__)) PEVNT_FOOTER;
+
+#endif
Index: /fact/FADctrl/FADctrl.cc
===================================================================
--- /fact/FADctrl/FADctrl.cc	(revision 10036)
+++ /fact/FADctrl/FADctrl.cc	(revision 10036)
@@ -0,0 +1,178 @@
+//
+//  FADctrl
+//
+
+#include <stdio.h>
+#include <readline/history.h>
+
+#include "FAD.h"
+
+const char READLINE_HIST_FILE[] = "/tmp/.history.FADctrl";
+
+void OpenOtherSockets();
+
+// ================
+//   Main program
+// ================
+
+int main() {
+
+  // Uses getc() for readline library (allows interruption by signal)
+  rl_getc_function = getc;
+  
+  // Load history buffer
+  read_history(READLINE_HIST_FILE);
+
+  system("clear");
+  printf("\n*** FADctrl (built %s, %s, revision %s) *** \n\n",__DATE__, __TIME__, REVISION);
+
+  // Construct main instance (static ensures destructor is called with exit())
+  static FAD M;
+
+  // Do not kill process if writing to closed socket
+  signal(SIGPIPE,SIG_IGN);
+
+  // Create thread to connect to other sockets
+  int RetVal;
+  pthread_t Thread;
+  if ((RetVal = pthread_create(&Thread, NULL, (void * (*)(void *)) OpenOtherSockets, NULL)) != 0) {
+    printf("pthread_create() failed for OpenOtherSockets(%s)\n", strerror(RetVal));
+	exit(EXIT_FAILURE);
+  }
+
+  // Initialise all boards
+  M.PrintMessage("Initalizing all boards...\n");
+
+  DimClient::sendCommand(SERVER_NAME"/Command", "dwrite off");
+  DimClient::sendCommand(SERVER_NAME"/Command", "domino off");
+  sleep(1);
+
+  DimClient::sendCommand(SERVER_NAME"/Command", "dac 0 21000");
+  DimClient::sendCommand(SERVER_NAME"/Command", "dac 1 0");
+  DimClient::sendCommand(SERVER_NAME"/Command", "dac 2-3 5000");
+  DimClient::sendCommand(SERVER_NAME"/Command", "dac 4-7 28800");
+  sleep (1);
+
+  DimClient::sendCommand(SERVER_NAME"/Command", "roi all 10");
+  DimClient::sendCommand(SERVER_NAME"/Command", "address 44 29");
+  sleep (1);
+
+  DimClient::sendCommand(SERVER_NAME"/Command", "trigger");
+  sleep (1);
+
+  DimClient::sendCommand(SERVER_NAME"/Command", "address 44 30");
+  sleep (1);
+
+  DimClient::sendCommand(SERVER_NAME"/Command", "trigger");
+  sleep (1);
+
+  DimClient::sendCommand(SERVER_NAME"/Command", "address 44 0");
+  sleep (1);
+
+  DimClient::sendCommand(SERVER_NAME"/Command", "trigger");
+  DimClient::sendCommand(SERVER_NAME"/Command", "domino on");
+  DimClient::sendCommand(SERVER_NAME"/Command", "dwrite on");
+  DimClient::sendCommand(SERVER_NAME"/Command", "roi all 1024");
+  sleep (1);
+
+  //EmptySockets(SocketDescriptor, 8, 750000L);
+  M.PrintMessage("Finished initalizing all boards\n");
+  
+  // Command loop
+  char *Command;
+  std::string LastHist;
+
+  while (!M.ExitRequest) {
+    Command = readline("\rFADctrl>");
+
+	// Check for interruption by signal
+    if (Command == NULL) continue;
+
+	// Add command to history
+    if(strlen(Command) > 0 && LastHist != Command) {
+	  add_history(Command);
+	  LastHist = Command;
+	}
+
+    // Process command
+	DimClient::sendCommand(SERVER_NAME"/Command", Command);
+    free(Command);
+  }
+  
+  // Save history buffer  
+  int Ret = write_history(READLINE_HIST_FILE);
+  if (Ret != 0 ) printf("Error writing history file to '%s' (%s)\n", READLINE_HIST_FILE, strerror(Ret));
+
+  // Terminate thread for other sockets  
+  if ((Ret = pthread_cancel(Thread)) != 0) printf("Error: Could not request thread cancellation (%s)\n", strerror(Ret));
+  if ((Ret = pthread_join(Thread, NULL)) != 0) printf("pthread_join() failed (%s)\n", strerror(Ret));
+}
+
+
+// ====================
+//   OpenOtherSockets()
+// ====================
+
+void OpenOtherSockets() {
+
+  static char Hostname[] = "192.33.99.225";
+  int List[] = {5001, 5002, 5003, 5004, 5005, 5006, 5007};
+  int Socket[sizeof(List)/sizeof(int)], MaxSocketNum=0, Ret;
+  fd_set DescriptorList;
+  char Buffer[1000000];
+
+  // Resolve hostname
+  struct hostent *Host = gethostbyname(Hostname);
+  if (Host == 0) {
+    printf("OtherSockets: Could not resolve host name for %s\n", Hostname);
+    return;
+  }
+
+  // Connect to server
+  struct sockaddr_in SocketAddress;
+  SocketAddress.sin_family = PF_INET;
+  SocketAddress.sin_addr = *(struct in_addr*) Host->h_addr;
+
+  for (unsigned int i=0; i<sizeof(List)/sizeof(int); i++) {
+	// Open socket descriptor
+	if ((Socket[i] = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+      printf("OtherSockets: Could not open socket for port %d (%s)\n", List[i], strerror(errno));
+      return;
+	}
+
+	// Determine highest socket number for select()
+	if (Socket[i] > MaxSocketNum) MaxSocketNum = Socket[i];
+	 
+	// Connect to server
+    SocketAddress.sin_port = htons((unsigned short) List[i]);
+	if (connect(Socket[i], (struct sockaddr *) &SocketAddress, sizeof(SocketAddress)) == -1) {
+      printf("OtherSockets: Could not connect to port %d (%s)\n", List[i], strerror(errno));
+      return;
+	}
+  }
+  
+  while(true) {
+    // Wait for data from terminal (stdin) or from sockets
+    FD_ZERO(&DescriptorList);   
+    for (unsigned int i=0; i<sizeof(List)/sizeof(int); i++) FD_SET(Socket[i], &DescriptorList);
+    if (select(MaxSocketNum+1, &DescriptorList, NULL, NULL, NULL) == -1) {
+      perror("OtherSockets: Error with select()");
+      break;
+    }
+	
+	// Data from socket
+	for (unsigned int i=0; i<sizeof(List)/sizeof(int); i++) if (FD_ISSET(Socket[i], &DescriptorList)) {
+	  Ret = read(Socket[i], Buffer, sizeof(Buffer));
+      if(Ret == 0) printf("OtherSockets: Connection to port %d not existing anymore\n", List[i]);
+      else if (Ret == -1) printf("OtherSockets: Error reading from port %d (%s)\n", List[i], strerror(errno));
+      else printf("OtherSockets: Read %d bytes from port %d\n", Ret, List[i]);
+    }
+  }
+
+  // Close all sockets
+  for (unsigned int i=0; i<sizeof(List)/sizeof(int); i++) {
+	if ((Socket[i] != -1) && (close(Socket[i]) == -1)) {
+	  printf("OtherSockets: Could not close socket of port %d (%s)", List[i], strerror(errno));  
+	}
+  }
+}
Index: /fact/FADctrl/History.txt
===================================================================
--- /fact/FADctrl/History.txt	(revision 10036)
+++ /fact/FADctrl/History.txt	(revision 10036)
@@ -0,0 +1,3 @@
+Version history of FADctrl
+
+22/10/2010	First check-in of FADctrl. Initial version derived from drsdaq revision 10007.
Index: /fact/FADctrl/Makefile
===================================================================
--- /fact/FADctrl/Makefile	(revision 10036)
+++ /fact/FADctrl/Makefile	(revision 10036)
@@ -0,0 +1,38 @@
+#
+#  Makefile for the FADctrl 
+#
+
+CC  	= g++
+
+SOURCES = FAD.cc FADctrl.cc FADBoard.cc ../Evidence/Evidence.cc 
+OBJECTS = $(addsuffix .o, $(basename $(SOURCES)))
+INCDIRS   = -I. -I../Evidence -I$(DIMDIR)/dim
+
+REVISION = $(shell svnversion -n)
+
+CPPFLAGS = -DREVISION='"$(REVISION)"' -O3 -Wall
+LIBS = -L /usr/lib/termcap -lstdc++ -lz -lpthread -lreadline -ltermcap $(DIMDIR)/linux/libdim.a
+
+FADctrl: $(OBJECTS)
+	$(CC) $(CPPFLAGS) -o $@ $(OBJECTS) $(LIBS)
+
+clean:
+	@rm -f $(OBJECTS)
+	@rm -f *.d
+	@rm -f *~
+	@rm -f FADctrl
+
+-include Dep.d
+	
+# Implicit rules
+
+%.o : %.c
+	$(CC) $(CPPFLAGS) $(INCDIRS) -c -o $@ $<
+%.o : %.cc
+	$(CC) $(CPPFLAGS) $(INCDIRS) -c -o $@ $<
+%.o : %.cpp
+	$(CC) $(CPPFLAGS) $(INCDIRS) -c -o $@ $< 
+%.d : 
+	@echo "Generating dependencies" $@
+	@$(CC) -MM $(SOURCES) $(INCDIRS) \
+	| sed 's/^\(.*\).o:/$@ \1.o:/' > $@
