Index: Evidence/Alarm.cc
===================================================================
--- Evidence/Alarm.cc	(revision 154)
+++ Evidence/Alarm.cc	(revision 159)
@@ -109,10 +109,8 @@
 	
 	// Update State: unavailable or current severity of status  
-	if (getInfo()->getSize()==strlen(NO_LINK)+1 &&
-		strcmp(getInfo()->getString(), NO_LINK)==0) State[i] = 4;
+	if (!ServiceOK(getInfo())) State[i] = 4;
 	else {
 	  State[i] = *(getInfo()->getString()+getInfo()->getSize()-1);
 
-  printf("*** String length: %d  Message length: %d    Last number: %d\n",strlen(getInfo()->getString()),getInfo()->getSize(),State[i]);
 	  // Print message
 	  time_t RawTime = getInfo()->getTimestamp();
Index: Evidence/DColl.cc
===================================================================
--- Evidence/DColl.cc	(revision 154)
+++ Evidence/DColl.cc	(revision 159)
@@ -38,4 +38,6 @@
 #include <regex.h>
 
+using namespace std;
+
 //
 // Class declaration
@@ -47,6 +49,6 @@
 	  DimStampedInfo *DataItem;
       DimService *HistService;
-	  struct EvidenceHistoryItem *HistBuffer;
-	  int HistPointer;
+	  char *Buffer;
+	  int Next;
 	  double LastValue;
 	  double MinAbsChange;
@@ -101,14 +103,9 @@
   // Request configuration data
   char *Change = GetConfig(SERVER_NAME " minchange");
-
   DataDir = GetConfig(SERVER_NAME " datadir");
-
   char *Logname = GetConfig(SERVER_NAME " logfile");
-
   SizeUpdateDelay = atoi(GetConfig(SERVER_NAME " sizeupdate"));
-
   HistSize = atoi(GetConfig(SERVER_NAME " histsize"));
-  if (HistSize < 1) HistSize = 1; // Minimum one items
-
+  if (HistSize < 3*sizeof(int)) HistSize = 3*sizeof(int);
   HistDir = GetConfig(SERVER_NAME " histdir");
    
@@ -163,6 +160,4 @@
   // Subscribe to list of servers at DIS_DNS 
   AddService("DIS_DNS/SERVER_LIST");
-
-  DimClient::sendCommand("DColl/Log", SERVER_NAME" *** Logging started ***");
 }
 
@@ -171,6 +166,4 @@
 //
 DataHandler::~DataHandler() {
-
-  DimClient::sendCommand("DColl/Log", SERVER_NAME" *** Logging stopped ***");
 
   // Delete DIM subscriptions and command first so handlers and not called anymore
@@ -179,6 +172,8 @@
   }
   delete LogCommand;
-  
+
   // Save history buffers to files (replace '/' by '_')
+  State(INFO, "Writing history buffers to files");
+
   for (int i=0; i<List.size(); i++) {
     string Name = List[i].HistService->getName();
@@ -186,10 +181,10 @@
     FILE *File = fopen((HistDir + "/" + Name).c_str(), "wb");
 	if (File != NULL) {
-      fwrite(&List[i].HistPointer, sizeof(List[i].HistPointer), 1, File);
-      fwrite(List[i].HistBuffer, sizeof(EvidenceHistoryItem), HistSize, File);
+      fwrite(&List[i].Next, sizeof(List[i].Next), 1, File);
+      fwrite(List[i].Buffer, sizeof(char), HistSize, File);
       fclose(File);
 	}
 	delete List[i].HistService;
-    delete[] List[i].HistBuffer;
+    delete[] List[i].Buffer;
   }
 
@@ -223,9 +218,16 @@
 void DataHandler::infoHandler() {
 
+  static const int WrapMark[] = {0, -1};
+  static const int EndMark[] = {0, 0};
+
   DimInfo *Info = getInfo();
 
   // Check if service available
-  if (Info->getSize()==strlen(NO_LINK)+1 && strcmp(Info->getString(), NO_LINK)==0) return;
-
+  if (!ServiceOK(Info)) return;
+
+  //
+  // ====== Part A: Handle service subscriptions ===
+  //
+  
   // If service is DIS_DNS/SERVER_LIST, subscribe to all SERVICE_LIST services
   if (strcmp(Info->getName(), "DIS_DNS/SERVER_LIST") == 0) {	
@@ -259,4 +261,8 @@
   }
 
+  //
+  // ====== Part B: Handle opening data files ===
+  //
+
   // If it is time to open new data file, close the current one 
   if (time(NULL) >= TimeForNextFile) {
@@ -296,12 +302,16 @@
 	TimeForNextFile = mktime(T);
   }
-   
+
+  //
+  // ====== Part C: Handle writing to data file ===
+  //
+
   // Identify index of service
-  int Service;  
+  int Service;
   for (Service=0; Service<List.size(); Service++) if (Info == List[Service].DataItem) break;
   if (Service == List.size()) return;  // Service not found
 
-  // If negative value for absolute change, ignore this entry
-  if (List[Service].MinAbsChange < 0) return;
+  // If negative value for absolute change, do not write to file
+  if (List[Service].MinAbsChange >= 0) {
 
   // Write data header
@@ -313,4 +323,5 @@
   // Translate data into ASCII
   char *Text = EvidenceServer::ToString(Info);
+
   if (Text != NULL) {
 	// Replace all control characters by white space
@@ -320,19 +331,8 @@
     fprintf(DataFile, "%s\n", Text);
 	
-	// Add to history buffer if change large enough
-	if ((fabs(atof(Text)-List[Service].LastValue) > List[Service].MinAbsChange)) {
-	  List[Service].HistBuffer[List[Service].HistPointer].Seconds = Info->getTimestamp();
-      List[Service].HistBuffer[List[Service].HistPointer].Value = atof(Text);
-      List[Service].HistService->updateService();
-	  List[Service].HistPointer++;
-	  if (List[Service].HistPointer >= HistSize) List[Service].HistPointer = 0;
-	  List[Service].LastValue = atof(Text);
-	}
 	free(Text);
   }
   else fprintf(DataFile, "Cannot interpret format identifier\n");
  
-  fflush(DataFile);
-
   // Terminate if error because otherwise infinite loop might result as
   // next call to this infoHandler() will try to (re-)open file
@@ -343,10 +343,76 @@
   }
 
-  // Update datafile size service
+  // Update datafile size service (not every time to avoid infinite loop)
   if (time(NULL) - DataSizeLastUpdate > SizeUpdateDelay) {
+    fflush(DataFile); // not continuously to reduce load
+
 	DataSizekB = FileSize(DataFile);
 	DataSizeService->updateService();
 	DataSizeLastUpdate = time(NULL);
   }
+  
+  } // Check for MinAbsChange
+
+  //
+  // ====== Part D: Handle history service ===
+  //
+  
+  if (Info->getSize() == 0) return;
+  
+  // Check if data should be added to history buffer
+  char *Text = EvidenceServer::ToString(Info);
+  if (Text != NULL && strcmp(Info->getFormat(),"C") != 0
+	  && fabs(atof(Text)-List[Service].LastValue) < fabs(List[Service].MinAbsChange)) {
+	free(Text);
+	return;
+  }
+  free(Text);
+
+  // Check if data fits into buffer
+  if (HistSize < Info->getSize() + 5*sizeof(int)) return;
+
+  int Size = Info->getSize() + 4*sizeof(int), Next = List[Service].Next;
+  void *WrapPos = NULL;
+  char *Buffer = List[Service].Buffer;
+  int Oldest = *(int *) Buffer;
+
+  // Check if buffer wrap-around (write wrap mark after Oldest is adjusted)
+  if (Next + Size >= HistSize) {
+    WrapPos = Buffer + Next;
+    Next = 4;
+  }
+
+  // Adapt pointer to oldest entry
+  while ((Oldest < Next + Size) && 
+  		 (Oldest + *((int *) (Buffer + Oldest) + 1) + 2*sizeof(int) > Next)) {
+	// Check for wrap-around
+	if (memcmp(Buffer + Oldest, WrapMark, sizeof(WrapMark)) == 0) {
+	  Oldest = 4;
+	  continue;
+	}
+	// Check if end marker reached, then only one event fits buffer
+  	if (memcmp(Buffer + Oldest, EndMark, sizeof(EndMark)) == 0) {
+	  Oldest = Next;
+	  break;
+	}
+	// Move to next entry
+    Oldest += *((int *) (Buffer + Oldest) + 1) + 2*sizeof(int);
+  }
+  // Update pointer in buffer
+  *(int *) Buffer = Oldest;
+
+  // Write wrap mark if necessary
+  if (WrapPos != NULL) memcpy(WrapPos, WrapMark, sizeof(WrapMark));
+
+  // Copy data into ring buffer
+  *((int *) (Buffer + Next)) = Info->getTimestamp();
+  *((int *) (Buffer + Next + sizeof(int))) = Info->getSize();
+  memcpy(Buffer + Next + 2*sizeof(int), Info->getData(), Info->getSize());
+
+  // Adjust pointer for next entry and write end marker to buffer
+  Next += Info->getSize() + 2*sizeof(int);
+  memcpy(Buffer + Next, EndMark, sizeof(EndMark));
+  
+  List[Service].Next = Next;
 }
 
@@ -403,5 +469,4 @@
   }
   
-  // Create new entry in item list
   struct Item New;
 
@@ -413,13 +478,14 @@
 	}
   }
-  		
+
   // Create history service
-  New.HistBuffer = new struct EvidenceHistoryItem [HistSize];
-  memset(New.HistBuffer, 0, HistSize*sizeof(EvidenceHistoryItem));
-  New.HistPointer = 0;
+  New.Buffer = new char [HistSize];
+  memset(New.Buffer, 0, HistSize);
+  *(int *) New.Buffer = 4;
+  New.Next = 4;
   New.LastValue = DBL_MAX;
   New.HistService = new DimService ((Name+".hist").c_str(), (char *) "C",
-  					  New.HistBuffer, HistSize*sizeof(EvidenceHistoryItem));
-	
+  					  New.Buffer, HistSize);
+
   // Load history buffer from file if existing
   string Filename = New.HistService->getName();
@@ -427,7 +493,10 @@
   FILE *File = fopen((HistDir + "/" + Filename).c_str(), "rb");
   if (File != NULL) {
-    fread(&New.HistPointer, sizeof(New.HistPointer), 1, File);
-    fread(New.HistBuffer, sizeof(EvidenceHistoryItem), HistSize, File);
-    fclose(File);
+    // Only load if current buffer size if equal or larger
+    if (FileSize(File) <= HistSize*sizeof(char)+sizeof(New.Next) && FileSize(File) != -1) {
+      fread(&New.Next, sizeof(New.Next), 1, File);
+      fread(New.Buffer, sizeof(char), HistSize, File);
+      fclose(File);
+	}
   }
   
Index: Evidence/Edd/Edd.cc
===================================================================
--- Evidence/Edd/Edd.cc	(revision 154)
+++ Evidence/Edd/Edd.cc	(revision 159)
@@ -9,5 +9,5 @@
 a DIM status service
 
-December 2009, Oliver Grimm
+February 2010, Oliver Grimm
 
 ============================================================ */
@@ -35,5 +35,5 @@
   
   // Connect to DIM handler
-  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QString, QByteArray, QString)), SLOT(Update(DimInfo*, int, QString, QByteArray, QString))) == false) {
+  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QByteArray, QString)), SLOT(Update(DimInfo*, int, QByteArray, QString))) == false) {
     printf("Failed connection for %s\n", DIMService.toAscii().data());
   }
@@ -43,7 +43,8 @@
   Menu->addAction("Open history", this, SLOT(MenuOpenHistory()));
   Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
+  Menu->addAction("Copy data", this, SLOT(MenuCopyData()));
 
   // DIM client
-  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, (char *) NO_LINK, Handler);
+  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, NO_LINK, Handler);
 }
 
@@ -54,7 +55,7 @@
 
 // Update widget
-void Edd_Indicator::Update(DimInfo *Info, int Time, QString Format, QByteArray Data, QString Text) {
-
-  if (Info != this->Data) return;
+void Edd_Indicator::Update(DimInfo *Info, int Time, QByteArray Array, QString Text) {
+
+  if (Info != Data) return;
     
   QPalette Pal = palette();  
@@ -67,15 +68,12 @@
   }
   else {
-    // If this is a status indicator, adapt background colour
-    if (Data.size() == Text.size()+2) {
-      switch (Data[Text.size() + 1]) {
-        case 0:  Pal.setColor(QPalette::Base, Qt::white); break;
-        case 1:  Pal.setColor(QPalette::Base, Qt::yellow); break;
-        case 2:  Pal.setColor(QPalette::Base, Qt::red); break;
-        case 3:  Pal.setColor(QPalette::Base, Qt::red); break;
-        default: break;
-      }
+    // Backgound colour determined by last byte
+    switch (Array[Array.size()]) {
+      case 0:  Pal.setColor(QPalette::Base, Qt::white); break;
+      case 1:  Pal.setColor(QPalette::Base, Qt::yellow); break;
+      case 2:  Pal.setColor(QPalette::Base, Qt::red); break;
+      case 3:  Pal.setColor(QPalette::Base, Qt::red); break;
+      default: break;
     }
-    else Pal.setColor(QPalette::Base, Qt::white);
 	
 	if (!ShowAsTime) setText(Text);
@@ -83,5 +81,5 @@
 	
     // Update status tip
-    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Format));
+    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Info->getFormat()));
   }
   
@@ -134,5 +132,5 @@
 }
 
-// Open history plot
+// Menu: Open history plot
 void Edd_Indicator::MenuOpenHistory() {
   
@@ -141,8 +139,14 @@
 }
 
-// Copy service name
+// Menu: Copy service name
 void Edd_Indicator::MenuCopyService() {
   
   QApplication::clipboard()->setText(QString(Data->getName()));
+}
+
+// Menu: Copy data
+void Edd_Indicator::MenuCopyData() {
+  
+  QApplication::clipboard()->setText(text());
 }
 
@@ -154,5 +158,5 @@
 // Constructor
 //
-Edd_Plot::Edd_Plot(QString DIMService, QWidget *P): QwtPlot(P) {
+Edd_Plot::Edd_Plot(QString DIMService, QWidget *P): QwtPlot(P), EvidenceHistory() {
 
   setAcceptDrops(true);
@@ -180,5 +184,5 @@
   
   // Connect to DIM handler
-  if (connect(Handler, SIGNAL(YEP(DimInfo *, int, QString, QByteArray, QString)), SLOT(Update(DimInfo *, int, QString, QByteArray, QString))) == false) {
+  if (connect(Handler, SIGNAL(YEP(DimInfo *, int, QByteArray, QString)), SLOT(Update(DimInfo *, int, QByteArray, QString))) == false) {
     printf("Failed connection for %s\n", DIMService.toAscii().data());
   }
@@ -211,9 +215,6 @@
 
   for (int i=0; i<Items.size(); i++) {
-    delete Items[i].Data;
     delete Items[i].LiveData;
     delete Items[i].Signal;
-    delete[] Items[i].x;
-    delete[] Items[i].y;
   }  
   delete Grid;
@@ -225,13 +226,11 @@
 void Edd_Plot::AddService(QString Name) {
 
-  QString HistName = Name+".hist";
-
   // Lock before accessing Items list
   QMutexLocker Locker(&Mutex);
 
-  // Check if already subsribed to service
+  // Check if already subscribed to service
   for (int i=0; i<Items.size(); i++) {
-    if (HistName == Items[i].Data->getName()) {
-      QMessageBox::warning(this, "Edd Message",HistName+" already present",QMessageBox::Ok);
+    if (Name == Items[i].LiveData->getName()) {
+      QMessageBox::warning(this, "Edd Message",Name+".hist already present",QMessageBox::Ok);
       return;
     }
@@ -242,11 +241,8 @@
   New.Signal = new QwtPlotCurve;
   New.Signal->attach(this);
-  New.Signal->setTitle(HistName);
+  New.Signal->setTitle(Name+".hist");
   New.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
-  New.x = NULL;
-  New.y = NULL;
-  New.Count = 0;
-  New.Data = new DimStampedInfo(HistName.toAscii().data(), (char *) NO_LINK, Handler);
-  New.LiveData = new DimStampedInfo(Name.toAscii().data(), (char *) NO_LINK, Handler); 
+  New.SizeLimit = 5000;
+  New.LiveData = new DimStampedInfo(Name.toAscii().data(), NO_LINK, Handler); 
 
   Items.append(New);
@@ -254,5 +250,5 @@
 
 // Update widget (must happen in GUI thread)
-void Edd_Plot::Update(DimInfo *Info, int Time, QString Format, QByteArray Data, QString Text) {
+void Edd_Plot::Update(DimInfo *Info, int Time, QByteArray, QString Text) {
 
   // Check if service available
@@ -266,59 +262,58 @@
   // Determine which plot item this call belongs to
   int ItemNo;
-  for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Info == Items[ItemNo].Data) {
-    // This is a history service  
-    EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) Data.data();
-    int Count=0, DataPoints = Data.size()/sizeof(struct EvidenceHistoryItem);
-
-    delete[] Items[ItemNo].x;
-    delete[] Items[ItemNo].y;
-    Items[ItemNo].x = new int [DataPoints];
-    Items[ItemNo].y = new double [DataPoints];
-
-    // Find oldest item
-    int Oldest;
-    for (Oldest=1; Oldest<DataPoints; Oldest++) {
-      if (Curr[Oldest].Seconds < Curr[Oldest-1].Seconds) break;
+  for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Info == Items[ItemNo].LiveData) {
+  
+	// If size limit reached, clear buffer
+    if (Items[ItemNo].x.size() > Items[ItemNo].SizeLimit) {
+	  Items[ItemNo].x.clear();
+	  Items[ItemNo].y.clear();
     }
 
-    // Plot data starting with oldest value
-    for (int i=0; i<DataPoints; i++) {
-      Items[ItemNo].x[Count] = Curr[(i+Oldest)%DataPoints].Seconds;
-      Items[ItemNo].y[Count] = Curr[(i+Oldest)%DataPoints].Value;
-      if (Items[ItemNo].x[Count] != 0) Count++;
-    }
-
-    // Find smallest and largest item
-    double Smallest = DBL_MAX, Largest = DBL_MIN;
-    for (int i=0; i<Count; i++) {
-      if (Largest < Items[ItemNo].y[i]) Largest = Items[ItemNo].y[i];
-      if (Smallest > Items[ItemNo].y[i]) Smallest = Items[ItemNo].y[i];
-    }
-    Items[ItemNo].Smallest = Smallest;
-    Items[ItemNo].Largest = Largest;
-    Items[ItemNo].Count = Count;
-
-    // Clear local history
-    Items[ItemNo].Live.clear();
+	// If buffer empty, request new history buffer
+    if (Items[ItemNo].x.isEmpty()) {
+	  int Time, Size;
+	  void *Data;
+
+	  if (GetHistory(Items[ItemNo].LiveData->getName())) {
+     	double Smallest = DBL_MAX, Largest = DBL_MIN;
+		double Number=0;
+		while (Next(Time, Size, Data)) {
+		  switch (*(Info->getFormat())) {
+    		case 'I':  Number = *(int *) Data;   break;
+    		case 'S':  Number = *(short *) Data;   break;
+    		case 'F':  Number = *(float *) Data;   break;
+    		case 'D':  Number = *(double *) Data;   break;
+    		case 'X':  Number = *(long long *) Data;   break;
+    		default: break;
+		  }
+
+		  Items[ItemNo].x.append(Time);
+		  Items[ItemNo].y.append(Number);
+		  
+      	  if (Largest < Items[ItemNo].y.last()) Largest = Items[ItemNo].y.last();
+    	  if (Smallest > Items[ItemNo].y.last()) Smallest = Items[ItemNo].y.last();
+		}
+
+    	Items[ItemNo].Smallest = Smallest;
+    	Items[ItemNo].Largest = Largest;
+
+		// Local buffer always at least twice as large as history
+		if (Items[ItemNo].SizeLimit < 2*Items[ItemNo].x.size()) {
+		  Items[ItemNo].SizeLimit = 2*Items[ItemNo].x.size();
+		}		
+	  }
+	}
+
+    // Append data
+    Items[ItemNo].x.append(Time);
+    Items[ItemNo].y.append(atof(Text.toAscii().data()));
+
+    // Update largest and smallest value    
+    if (Items[ItemNo].y.last() > Items[ItemNo].Largest) Items[ItemNo].Largest = Items[ItemNo].y.last();
+    if (Items[ItemNo].y.last() < Items[ItemNo].Smallest) Items[ItemNo].Smallest = Items[ItemNo].y.last();    
 
     // Update status tip
     QDateTime Timex = QDateTime::fromTime_t(Time); 
-    StatusTip = QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), Timex.toString()).arg(Format);
-
-  } else if (Info == Items[ItemNo].LiveData) {
-    // This is a live service 
-
-    // Limit size of list
-    if (Items[ItemNo].Live.size() > 1000) Items[ItemNo].Live.removeFirst();
-
-    // Append data
-    struct EvidenceHistoryItem Data;
-    Data.Seconds = Time;
-    Data.Value = atof(Text.toAscii().data());            
-    Items[ItemNo].Live.append(Data);
-
-    // Update largest and smallest value    
-    if (Data.Value > Items[ItemNo].Largest) Items[ItemNo].Largest = Data.Value;
-    if (Data.Value < Items[ItemNo].Smallest) Items[ItemNo].Smallest = Data.Value;    
+    StatusTip = QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), Timex.toString()).arg(Info->getFormat());
   }
 
@@ -352,5 +347,5 @@
     else Items[ItemNo].Signal->setSymbol(Sym1);
 
-    int DataPoints = Items[ItemNo].Count + Items[ItemNo].Live.size();
+    int DataPoints = Items[ItemNo].x.size();
 	
 	if (DataPoints == 0) continue;
@@ -361,20 +356,16 @@
     // Adapt time scale and normalize y scale if requested
     for (int i=0; i<DataPoints; i++) {
-      if (i < Items[ItemNo].Count) {
-        x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55200;
-        y[i] = Items[ItemNo].y[i];
-      }
-      else {
-        x[i]= Items[ItemNo].Live[i-Items[ItemNo].Count].Seconds / 86400.0 + 40587.5 - 55200;
-        y[i] = Items[ItemNo].Live[i-Items[ItemNo].Count].Value;
-      }
+      x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55200;
+      y[i] = Items[ItemNo].y[i];
 
       if (NormAction->isChecked()) {
-	if (Items[ItemNo].Smallest != Items[ItemNo].Largest) y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
-	else y[i] = 1;
+	    if (Items[ItemNo].Smallest != Items[ItemNo].Largest) {
+		  y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
+		}
+	    else y[i] = 1;
       }  
     }
 
-    // Plot datas
+    // Plot data
     Items[ItemNo].Signal->setData(x, y, DataPoints);
     Items[ItemNo].Signal->show();
@@ -443,9 +434,6 @@
 
   while (Items.size() > 1) {  
-    delete Items.last().Data;
     delete Items.last().LiveData;
     delete Items.last().Signal;
-    delete[] Items.last().x;
-    delete[] Items.last().y;
     Items.takeLast();
   }
@@ -473,7 +461,7 @@
    // Write x and y data for all signals to file
   for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
-    Stream << QString("# ")+Items[ItemNo].Data->getName() << endl;
+    Stream << QString("# ")+Items[ItemNo].LiveData->getName()+".hist" << endl;
     for (int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
-      Stream << (int) Items[ItemNo].x[i] << " " << Items[ItemNo].Signal->y(i) << endl;
+      Stream << (int) Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
     }
   }
@@ -514,4 +502,69 @@
 
 
+///////////////////////////////////////
+// History text box for DIM service //
+//////////////////////////////////////
+
+//
+// Constructor
+//
+Edd_TextHist::Edd_TextHist(QString DIMService, QWidget *P): QTextEdit(P), EvidenceHistory() {
+  
+  // Widget properties
+  setReadOnly(true);
+  setAttribute(Qt::WA_DeleteOnClose);
+  setAutoFillBackground(true);
+  document()->setMaximumBlockCount(1000);
+  
+  // Connect to DIM handler
+  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QByteArray, QString)), SLOT(Update(DimInfo*, int, QByteArray, QString))) == false) {
+    printf("Failed connection for %s\n", DIMService.toAscii().data());
+  }
+
+  // Get history for this service 
+  int Time, Size;
+  void *Data;
+
+  if (GetHistory(DIMService.toAscii().data())) {
+	while (Next(Time, Size, Data)) {
+	  moveCursor (QTextCursor::Start);
+	  insertPlainText(QString("(")+QDateTime::fromTime_t(Time).toString()+") ");	  
+	  insertPlainText(QString((char *) Data) + "\n");	  
+	}
+  }
+
+  // DIM client
+  Service = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, NO_LINK, Handler);
+}
+
+// Destructor
+Edd_TextHist::~Edd_TextHist() {
+
+  delete Service;
+}
+
+
+// Update widget (must happen in GUI thread)
+void Edd_TextHist::Update(DimInfo *Info, int Time, QByteArray, QString Text) {
+
+  if (Info != this->Service) return;
+
+  // Check if service available
+  if (Time == -1) {
+	setStatusTip(QString("%1:  unavailable").arg(Info->getName()));
+	return;
+  }
+
+  QDateTime Timex = QDateTime::fromTime_t(Time); 
+
+  moveCursor(QTextCursor::Start);
+  insertPlainText(QString("(")+Timex.toString()+QString(") "));	  
+  insertPlainText(Text + "\n");	  
+	
+  // Update status tip
+  StatusTip = QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), Timex.toString()).arg(Info->getFormat());
+}
+
+
 //////////////////
 // Text display //
@@ -528,10 +581,10 @@
   
   // Connect to DIM handler
-  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QString, QByteArray, QString)), SLOT(Update(DimInfo*, int, QString, QByteArray, QString))) == false) {
+  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QByteArray, QString)), SLOT(Update(DimInfo*, int, QByteArray, QString))) == false) {
     printf("Failed connection for %s\n", DIMService.toAscii().data());
   }
 
   // DIM client
-  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, (char *) NO_LINK, Handler);
+  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, NO_LINK, Handler);
 }
 
@@ -543,5 +596,5 @@
 
 // Handling of DIM service update
-void Edd_Textout::Update(DimInfo *Info, int Time, QString Format, QByteArray, QString Text) {
+void Edd_Textout::Update(DimInfo *Info, int Time, QByteArray, QString Text) {
 
   if (Info != this->Data) return;
@@ -561,8 +614,8 @@
 	
     // Add if service contains only a string
-    if (Format == "C") insertPlainText(Text);
+    if (strcmp(Info->getFormat(), "C") == 0) insertPlainText(Text);
 
     // Update status tip
-    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Format));
+    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Info->getFormat()));
   }
   setPalette(Pal);
@@ -652,4 +705,12 @@
   MainLayout->addWidget(Value, 7, 0, 1, 1);
 
+  QPushButton *Button = new QPushButton();
+  Button->setText("Start DIM browser");
+  connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser()));
+  MainLayout->addWidget(Button, 7, 1, 1, 1);
+
+  Edd_TextHist *Bla;
+  Bla = new Edd_TextHist("DColl/CurrentFile");
+  MainLayout->addWidget(Bla, 8, 0, 1, 1);
 
   // Layout of all widgets
@@ -669,4 +730,5 @@
     Text = Text.sprintf("Bias/VOLT/ID00/00-%.3d",i);
     Value = new Edd_Indicator(Text);
+
     BiasLayout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
     Graph->AddService(Text);
@@ -686,5 +748,5 @@
   Textout->setFixedWidth(400);
   BiasLayout->addWidget(Textout, 10, 0, 4, 4);      
-  
+
   // Environment page
   EnvironmentWidget = new QWidget();
@@ -772,14 +834,19 @@
 
   // Check if service available
-  if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
-    YEP(getInfo(), -1);
-  }
+  if (!EvidenceServer::ServiceOK(getInfo())) YEP(getInfo(), -1);
   else {
     char *Txt = EvidenceServer::ToString(getInfo());
 
-    YEP(getInfo(), getInfo()->getTimestamp(), getInfo()->getFormat(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), QString(Txt));
-		free(Txt);
-  }
-}
+    YEP(getInfo(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), QString(Txt));
+	free(Txt);
+  }
+}
+
+// Start DIM Browser
+void GUI::StartDIMBrowser() {
+
+  QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/");
+}
+
 
 //---------------------------------------------------------------------
Index: Evidence/Edd/Edd.h
===================================================================
--- Evidence/Edd/Edd.h	(revision 154)
+++ Evidence/Edd/Edd.h	(revision 159)
@@ -46,24 +46,23 @@
 	
   private slots:
-	void Update(DimInfo *, int, QString, QByteArray, QString);
+	void Update(DimInfo *, int, QByteArray, QString);
     void contextMenuEvent(QContextMenuEvent *);    
     void MenuOpenHistory();
     void MenuCopyService();
+    void MenuCopyData();
 };
 
 // Graph class for history display
-class Edd_Plot: public QwtPlot, public DimClient {
+class Edd_Plot: public QwtPlot, public EvidenceHistory {
     Q_OBJECT
 
     struct PlotItem {
-      DimInfo *Data;
       DimInfo *LiveData;
       QwtPlotCurve *Signal;
-      int *x;
-      double *y;
-      int Count;
       double Smallest;
       double Largest;
-      QList<struct EvidenceHistoryItem> Live;
+	  int SizeLimit;
+	  QVector<double> x;
+	  QVector<double> y;	  
     };
 
@@ -93,5 +92,5 @@
   private slots:
     void UpdatePlot();
-	void Update(DimInfo* Info, int, QString, QByteArray, QString);
+	void Update(DimInfo* Info, int, QByteArray, QString);
 
     void HandleZoom(const QwtDoubleRect &);
@@ -106,6 +105,21 @@
 };
 
+// Text history class
+class Edd_TextHist: public QTextEdit, public EvidenceHistory {
+    Q_OBJECT
+
+	QString StatusTip;
+	DimStampedInfo *Service;
+	
+  public:
+    Edd_TextHist(QString, QWidget * = NULL);
+    ~Edd_TextHist();
+
+  private slots:
+	void Update(DimInfo* Info, int, QByteArray, QString);
+};
+
 // Textout indicator for DIM service
-class Edd_Textout: public QTextEdit, public DimClient {
+class Edd_Textout: public QTextEdit, public EvidenceHistory {
     Q_OBJECT
 
@@ -119,5 +133,5 @@
 	
   private slots:
-	void Update(DimInfo* Info, int, QString, QByteArray, QString);
+	void Update(DimInfo* Info, int, QByteArray, QString);
 };
 
@@ -143,7 +157,8 @@
     void MenuAbout();
     void MenuNewHistory();
+	void StartDIMBrowser();
 	
   signals:
-    void YEP(DimInfo *, int, QString = QString(), QByteArray = QByteArray(), QString = QString());
+    void YEP(DimInfo *, int, QByteArray = QByteArray(), QString = QString());
 };
 
Index: Evidence/Evidence.cc
===================================================================
--- Evidence/Evidence.cc	(revision 154)
+++ Evidence/Evidence.cc	(revision 159)
@@ -25,4 +25,10 @@
 #include "Evidence.h"
 
+using namespace std;
+
+//////////////////////////
+// EvidenceServer Class //
+//////////////////////////
+
 EvidenceServer *ThisServer;
 
@@ -50,5 +56,4 @@
   start(Name);
   addExitHandler(this);
-
 }
 
@@ -56,4 +61,6 @@
 EvidenceServer::~EvidenceServer() {
 
+  State(INFO, "Server stopped");
+  
   for (unsigned int i=0; i<ConfigList.size(); i++) {
   	delete[] ConfigList[i].Name;
@@ -64,5 +71,5 @@
 // DIM exit handler
 void EvidenceServer::exitHandler(int Code) {
-  State(INFO, "Server stopped (DIM exit code %d)", Code);
+  State(INFO, "Exit handler called (DIM exit code %d)", Code);
   exit(EXIT_SUCCESS);
 }
@@ -104,5 +111,5 @@
   // Send message to console and log file
   printf("%s\n", TBuf);
-  if (Severity != INFO) DimClient::sendCommand("DColl/Log", TBuf);
+  DimClient::sendCommandNB("DColl/Log", TBuf);
 
   // Update DIM status service (including severity encoding)
@@ -188,25 +195,104 @@
 
 // Translates DIMInfo to string (memory has to be freed by caller)
-// No DIM structures are supported (only a single number or string is converted)
+// DIM structures are converted to hexadecimal representation
 // For string conversion, a terminating \0 is enforced.
 char *EvidenceServer::ToString(DimInfo *Item) {
 
   char *Text;
-  int R;
-  
-  if (strlen(Item->getFormat()) != 1) return NULL;
-  
-  switch (*(Item->getFormat())) {
-    case 'I':  R = asprintf(&Text, "%d", Item->getInt());   break;
-    case 'S':  R = asprintf(&Text, "%hd", Item->getShort());   break;
-    case 'F':  R = asprintf(&Text, "%.5f", Item->getFloat());   break;
-    case 'D':  R = asprintf(&Text, "%.5f", Item->getDouble());   break;
-    case 'X':  R = asprintf(&Text, "%lld", Item->getLonglong());   break;
-    case 'C':  *(Item->getString() + Item->getSize()) = '\0'; 
-			   R = asprintf(&Text, "%s", Item->getString());
-			   break;
-    default: return NULL;
-  }
-
+  int R=0;
+  
+  if (strlen(Item->getFormat()) != 1) {
+    if ((Text = (char *) malloc(3*Item->getSize()+1)) != NULL) {
+	  for (int i=0; i<Item->getSize(); i++) sprintf(Text+3*i, "%02x", *((char *) Item->getData() + i)); 
+	}
+  }
+  else {
+	switch (*(Item->getFormat())) {
+      case 'I':  R = asprintf(&Text, "%d", Item->getInt());   break;
+      case 'S':  R = asprintf(&Text, "%hd", Item->getShort());   break;
+      case 'F':  R = asprintf(&Text, "%.5f", Item->getFloat());   break;
+      case 'D':  R = asprintf(&Text, "%.5f", Item->getDouble());   break;
+      case 'X':  R = asprintf(&Text, "%lld", Item->getLonglong());   break;
+      case 'C':  *(Item->getString() + Item->getSize() - 1) = '\0'; 
+				 R = asprintf(&Text, "%s", Item->getString());
+				 break;
+      default: return NULL;
+	}
+  }
+  
   return (R == -1) ? NULL : Text;
 }
+
+// Checks if service contents indicates not available
+bool EvidenceServer::ServiceOK(DimInfo *Item) {
+
+  return !((Item->getSize() == strlen(NO_LINK)+1) &&  
+	  (memcmp(Item->getData(), NO_LINK, Item->getSize()) == 0));
+
+}
+
+
+///////////////////////////
+// EvidenceHistory Class //
+///////////////////////////
+
+// Organisaztion of history buffer
+// F | T S D | T S D | 0 0 ......  | T S D | T S D | 0 -1
+//
+// F: Offset of oldest entry  T: Time  S: Size  D: Data
+// F, T, S: int
+
+// Marker for history buffer
+const int EvidenceHistory::WrapMark[] = {0, -1};
+const int EvidenceHistory::EndMark[] = {0, 0};
+
+// Constructor
+EvidenceHistory::EvidenceHistory() {
+
+  Buffer = NULL;
+}
+
+// Destructor
+EvidenceHistory::~EvidenceHistory() {
+
+  delete[] Buffer;
+}
+
+// Requests service history
+bool EvidenceHistory::GetHistory(char *Name) {
+
+  DimCurrentInfo Info((string(Name)+".hist").c_str(), NO_LINK);
+
+  // Check if service available
+  if (((Info.getSize() == strlen(NO_LINK)+1) &&  
+	  (memcmp(Info.getData(), NO_LINK, Info.getSize()) == 0))) return false;
+
+  delete[] Buffer;
+  BufferSize = Info.getSize();
+  Buffer = new char [BufferSize];
+
+  memcpy(Buffer, Info.getData(), BufferSize);
+  Offset = *(int *) Buffer;
+  
+  return true;
+}
+
+// Returns next item in history buffer
+bool EvidenceHistory::Next(int &Time, int &Size, void *&Data) {
+
+  if (Buffer == NULL) return false;
+
+  // Check for wrap around
+  if (memcmp(Buffer+Offset, WrapMark, sizeof(WrapMark)) == 0) Offset = 4;
+  
+  // Check if at end of ring buffer
+  if (memcmp(Buffer+Offset, EndMark, sizeof(EndMark)) == 0) return false;
+
+  Time = *(int *) (Buffer + Offset);
+  Size = *(int *) (Buffer + Offset + sizeof(int));
+  Data = Buffer + Offset + 2*sizeof(int);
+
+  Offset += *((int *) (Buffer + Offset) + 1) + 2*sizeof(int);
+
+  return true;
+}
Index: Evidence/Evidence.h
===================================================================
--- Evidence/Evidence.h	(revision 154)
+++ Evidence/Evidence.h	(revision 159)
@@ -15,12 +15,4 @@
 #define STATUS_SIZE 1000					// Bytes for status service string
 
-using namespace std;
-
-// Declaration of item for history buffer (see DColl.cc)
-struct EvidenceHistoryItem {
-  int Seconds;
-  double Value;
-} __attribute__((__packed__));
-
 // Class declation of Evidence server
 class EvidenceServer: public DimServer {
@@ -32,5 +24,5 @@
 	  int Time;
 	};
-	vector<struct ConfigItem> ConfigList;
+	std::vector<struct ConfigItem> ConfigList;
 
     DimService *Status;
@@ -50,7 +42,25 @@
 	char* GetConfig(const char *, const char * = NULL);
 	static char* ToString(DimInfo *);
+	static bool ServiceOK(DimInfo *);
 
     bool ExitRequest;
 };
 
+class EvidenceHistory: public DimClient {
+
+  static const int WrapMark[];
+  static const int EndMark[];
+
+  std::string HistoryName;
+  char *Buffer;
+  int BufferSize;
+  int Offset;
+    
+  public:
+	EvidenceHistory();
+	~EvidenceHistory();
+	
+	bool GetHistory(char *);
+	bool Next(int &, int &, void *&);
+};
 #endif
