From f97e14730cd9d2f9d129a5cc6fd84ee2a256ab99 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Thu, 16 Jul 2020 11:34:14 -0700
Subject: [PATCH 01/23] add CSV sink

---
 src/libDataSink/CMakeLists.txt |   1 +
 src/libDataSink/CSVSink.cpp    | 213 +++++++++++++++++++++++++++++++++
 src/libDataSink/CSVSink.h      | 138 +++++++++++++++++++++
 src/tools/ps_monitor.cpp       |   5 +-
 4 files changed, 355 insertions(+), 2 deletions(-)
 create mode 100644 src/libDataSink/CSVSink.cpp
 create mode 100644 src/libDataSink/CSVSink.h

diff --git a/src/libDataSink/CMakeLists.txt b/src/libDataSink/CMakeLists.txt
index 602e2bd3..fa757053 100644
--- a/src/libDataSink/CMakeLists.txt
+++ b/src/libDataSink/CMakeLists.txt
@@ -3,6 +3,7 @@ target_sources(DataSink
   PRIVATE
   IDataSink.cpp
   ConsoleSink.cpp
+  CSVSink.cpp
   DataSinkRegistry.cpp
   )
 target_link_libraries(DataSink PRIVATE Utils)
diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
new file mode 100644
index 00000000..3f9d30dc
--- /dev/null
+++ b/src/libDataSink/CSVSink.cpp
@@ -0,0 +1,213 @@
+#include "CSVSink.h"
+
+#include <algorithm>
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+
+#include "DataSinkRegistry.h"
+REGISTER_DATASINK(CSVSink)
+
+CSVSink::CSVSink(const std::string& name)
+  : IDataSink(name)
+{ }
+
+void CSVSink::setConfiguration(const nlohmann::json& config)
+{
+  for (const auto &kv : config.items())
+    {
+      if(kv.key()=="directory")
+        {
+          m_directory=kv.value();
+        }
+    }
+}
+
+void CSVSink::setTag(const std::string& name, const std::string& value)
+{
+  checkTag(name);
+  m_tagsString[name]=value;
+}
+
+void CSVSink::setTag(const std::string& name, int8_t value)
+{
+  checkTag(name);
+  m_tagsInt8[name]=value;
+}
+
+void CSVSink::setTag(const std::string& name, int32_t value)
+{
+  checkTag(name);
+  m_tagsInt32[name]=value;
+}
+
+void CSVSink::setTag(const std::string& name, int64_t value)
+{
+  checkTag(name);
+  m_tagsInt64[name]=value;
+}
+
+void CSVSink::setTag(const std::string& name, double value)
+{
+  checkTag(name);
+  m_tagsDouble[name]=value;
+}
+
+void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time_point<std::chrono::system_clock> time)
+{
+  // Clean-up last measurement
+  m_fields.clear();
+  m_fieldsString.clear();
+  m_fieldsInt8  .clear();
+  m_fieldsInt32 .clear();
+  m_fieldsInt64 .clear();
+  m_fieldsDouble.clear();
+
+  // State
+  m_finalSchema=false;
+  m_inMeasurement=true;
+
+  m_measurement=measurement;
+}
+
+void CSVSink::setField(const std::string& name, const std::string& value)
+{
+  addField(name);
+  m_fieldsString[name]=value;
+}
+
+void CSVSink::setField(const std::string& name, int8_t value)
+{
+  addField(name);
+  m_fieldsInt8[name]=value;
+}
+
+void CSVSink::setField(const std::string& name, int32_t value)
+{
+  addField(name);
+  m_fieldsInt32[name]=value;
+}
+
+void CSVSink::setField(const std::string& name, int64_t value)
+{
+  addField(name);
+  m_fieldsInt64[name]=value;
+}
+
+void CSVSink::setField(const std::string& name, double value)
+{
+  addField(name);
+  m_fieldsDouble[name]=value;
+}
+
+void CSVSink::recordPoint()
+{
+  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&m_dirty;
+
+  std::ofstream ofile;
+  ofile.open (m_directory+m_measurement+".csv", std::ios_base::app);
+
+  //
+  // Print the header
+  if(!same)
+    { // First time recording point for measurement, print header
+      printHeader(ofile);
+      m_dirty=false;
+    }
+
+  //
+  // Print the fields
+  for(const std::string& field : m_fields)
+    {
+      if(m_fieldsString.count(field)>0)
+	ofile << "," << m_fieldsString[field];
+
+      if(m_fieldsInt8.count(field)>0)
+	ofile << "," << m_fieldsInt8[field];
+
+      if(m_fieldsInt32.count(field)>0)
+        ofile << "," << m_fieldsInt32[field];
+
+      if(m_fieldsInt64.count(field)>0)
+        ofile << "," << m_fieldsInt64[field];
+      
+      if(m_fieldsDouble.count(field)>0)
+	ofile << "," << m_fieldsDouble[field];
+    }
+  ofile << "\n";
+
+  ofile.close();
+  // Update last configuration
+  m_lastMeasurement=m_measurement;
+  m_lastFields=m_fields;
+
+  m_finalSchema=true;
+}
+
+void CSVSink::endMeasurement()
+{
+  m_inMeasurement=false;
+}
+
+void CSVSink::checkTag(const std::string& name)
+{
+  if(checkReserved(name))
+    throw std::runtime_error("Tag name \""+name+"\" is reserved.");
+
+
+  if(std::find(m_tags.begin(), m_tags.end(), name)==m_tags.end())
+    {
+      if(m_inMeasurement)
+	throw std::runtime_error("CSVSink: Cannot change tag during measurement.");
+
+      m_tags.push_back(name);
+    }
+
+  m_dirty=true;
+}
+
+void CSVSink::addField(const std::string& name)
+{
+  if(checkReserved(name))
+    throw std::runtime_error("Tag name \""+name+"\" is reserved.");
+
+  if(std::find(m_fields.begin(), m_fields.end(), name)==m_fields.end())
+    {
+      if(m_finalSchema)
+	throw std::runtime_error("CSVSink: Cannot add new fields after the first recordPoint.");
+      m_fields.push_back(name);
+    }
+}
+
+void CSVSink::printHeader(std::ofstream& ofile)
+{
+  // Print out the tags
+  for(const std::string& tag : m_tags)
+    {
+      ofile << tag << ": ";
+
+      if(m_tagsString.count(tag)>0)
+	ofile << m_tagsString[tag];
+
+      if(m_tagsInt8.count(tag)>0)
+	ofile << m_tagsInt8   [tag];
+
+      if(m_tagsInt32.count(tag)>0)
+        ofile << m_tagsInt32   [tag];
+
+      if(m_tagsInt64.count(tag)>0)
+        ofile << m_tagsInt64   [tag];
+
+      if(m_tagsDouble.count(tag)>0)
+	ofile << m_tagsDouble[tag];
+
+      ofile << std::endl;
+    }
+
+  // Print field names
+  for(const std::string& field : m_fields)
+    {
+      ofile << "," << field;
+    }
+  ofile << "\n";
+}
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
new file mode 100644
index 00000000..3a4de1cc
--- /dev/null
+++ b/src/libDataSink/CSVSink.h
@@ -0,0 +1,138 @@
+#ifndef CSVSINK_H
+#define CSVSINK_H
+
+#include <vector>
+#include <unordered_map>
+
+#include "IDataSink.h"
+
+//! \brief Data sink that saves to CSV files
+/**
+ * When a data point is recorded, all fields are saved separated by a space
+ * The header (tags and the column names) are printed when any of the following happens:
+ *  - The name of the measurement changes.
+ *  - A tag value changes.
+ *  - The set of fields changes.
+ *
+ * The header is not printed just because a previous measurement has been stopped. If
+ * the field definitions and tags are the same, the previous header still applies.
+ */
+class CSVSink : public IDataSink
+{
+public:
+  CSVSink(const std::string& name);
+
+  /** \brief Configure file sink based on JSON object                       
+   *                                                                            
+   * Valid keys:                                                                
+   *  - `directory`: output directory where files are saved
+   *                                                                            
+   * \param config JSON configuration                                           
+   */
+  virtual void setConfiguration(const nlohmann::json& config);
+
+  virtual void setTag(const std::string& name, const std::string& value);
+  virtual void setTag(const std::string& name, int8_t  value);
+  virtual void setTag(const std::string& name, int32_t value);
+  virtual void setTag(const std::string& name, int64_t value);
+  virtual void setTag(const std::string& name, double value);
+
+  virtual void startMeasurement(const std::string& measurement, std::chrono::time_point<std::chrono::system_clock> time);
+  virtual void setField(const std::string& name, const std::string& value);
+  virtual void setField(const std::string& name, int8_t  value);
+  virtual void setField(const std::string& name, int32_t value);
+  virtual void setField(const std::string& name, int64_t value);
+  virtual void setField(const std::string& name, double value);
+
+  //! \brief Print data point
+  /**
+   * Prints the fields in the current data points. If the header
+   * needs to be reprinted, it is also done here.
+   */
+  virtual void recordPoint();
+  virtual void endMeasurement();
+
+private:
+  //! \brief Checks whether a tag can be added
+  /**
+   * If tag is not defined, then it is added to the list.
+   *
+   * Marks tags as dirty.
+   *
+   * Error checking is performed. An exception is thrown if
+   *  - Currently performing a measurement
+   *
+   * \param name Field name
+   */
+  void checkTag(const std::string& name);
+
+  //! \brief Adds a new field column if it already does not exists. 
+  /**
+   * If field is already defined, nothing is done.
+   *
+   * Error checking is performed. An exception is thrown if
+   *  - called after the first `recordPoint` in a measurement
+   *
+   * \param name Field name
+   */
+  void addField(const std::string& name);
+
+  //! \brief Print the header
+  /**
+   * Prints the header in the format.
+   *  Tag1: value
+   *  ...
+   *  Tagn: value
+   *  FIELD1 FIELD2 FIELD3
+   *
+   * \param ofile file to save the measurement
+   */
+  void printHeader(std::ofstream& ofile);
+
+  //
+  // Directory where files are saved
+  std::string m_directory;
+
+  //
+  // The last state (before endMeasurement)
+
+  // Name of the last measurement
+  std::string m_lastMeasurement;
+
+  // The last columns
+  std::vector<std::string> m_lastFields;
+
+  //
+  // State
+
+  // Name of the current measurement (valid onyl if performing one)
+  std::string m_measurement;
+
+  // Currently performing a measurement
+  bool m_inMeasurement=false;
+
+
+  //
+  // Data store for fields
+  bool m_finalSchema=false;  // The measurement schema is locked
+  std::vector<std::string> m_fields;
+  std::unordered_map<std::string, std::string> m_fieldsString;
+  std::unordered_map<std::string, int8_t     > m_fieldsInt8 ;
+  std::unordered_map<std::string, int32_t    > m_fieldsInt32;
+  std::unordered_map<std::string, int64_t    > m_fieldsInt64;
+  std::unordered_map<std::string, double     > m_fieldsDouble;
+
+
+  //
+  // Data store for tags
+  bool m_dirty=true; // Tag values have changed, reprint
+  std::vector<std::string> m_tags;
+  std::unordered_map<std::string, std::string> m_tagsString;
+  std::unordered_map<std::string, int8_t     > m_tagsInt8 ;
+  std::unordered_map<std::string, int32_t    > m_tagsInt32;
+  std::unordered_map<std::string, int64_t    > m_tagsInt64;
+  std::unordered_map<std::string, double     > m_tagsDouble;
+
+};
+
+#endif // CSVSINK_H
diff --git a/src/tools/ps_monitor.cpp b/src/tools/ps_monitor.cpp
index 3fd3a58b..2adbc3bc 100644
--- a/src/tools/ps_monitor.cpp
+++ b/src/tools/ps_monitor.cpp
@@ -85,8 +85,9 @@ int main(int argc, char*argv[])
   logger(logDEBUG) << " Config file: " << configFile;
   logger(logDEBUG) << " sink name: " << sinkName;
   logger(logDEBUG) << " PS channel: " << channelName;
-
-  // Crease sink
+  
+  //
+  // Create sink
   DataSinkConf ds;
   ds.setHardwareConfig(configFile);
   std::shared_ptr<IDataSink> sink = ds.getDataSink(sinkName);
-- 
GitLab


From 37f96715da81aead42d0fbea469956e1aaea8229 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Thu, 16 Jul 2020 11:36:45 -0700
Subject: [PATCH 02/23] remove space

---
 src/tools/ps_monitor.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/tools/ps_monitor.cpp b/src/tools/ps_monitor.cpp
index 2adbc3bc..525b0729 100644
--- a/src/tools/ps_monitor.cpp
+++ b/src/tools/ps_monitor.cpp
@@ -86,7 +86,6 @@ int main(int argc, char*argv[])
   logger(logDEBUG) << " sink name: " << sinkName;
   logger(logDEBUG) << " PS channel: " << channelName;
   
-  //
   // Create sink
   DataSinkConf ds;
   ds.setHardwareConfig(configFile);
-- 
GitLab


From 4c0641e308410f4d1fd64712e6451db662e96281 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Thu, 16 Jul 2020 12:59:20 -0700
Subject: [PATCH 03/23] add tags as columns

---
 src/libDataSink/CSVSink.cpp | 34 +++++++++++++++++++++++++++++++---
 1 file changed, 31 insertions(+), 3 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 3f9d30dc..d8e45b23 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -102,7 +102,7 @@ void CSVSink::setField(const std::string& name, double value)
 
 void CSVSink::recordPoint()
 {
-  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&m_dirty;
+  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&!m_dirty;
 
   std::ofstream ofile;
   ofile.open (m_directory+m_measurement+".csv", std::ios_base::app);
@@ -115,6 +115,30 @@ void CSVSink::recordPoint()
       m_dirty=false;
     }
 
+  bool first = true;
+  for(const std::string& tag : m_tags)
+    {
+      if (!(first == true)) ofile << ", ";
+
+      if(m_tagsString.count(tag)>0)
+        ofile << m_tagsString[tag];
+
+      if(m_tagsInt8.count(tag)>0)
+        ofile << m_tagsInt8   [tag];
+
+      if(m_tagsInt32.count(tag)>0)
+	ofile << m_tagsInt32   [tag];
+
+      if(m_tagsInt64.count(tag)>0)
+        ofile << m_tagsInt64   [tag];
+
+      if(m_tagsDouble.count(tag)>0)
+	ofile << m_tagsDouble[tag];
+
+      if (first == true) first = false;
+      
+    }
+
   //
   // Print the fields
   for(const std::string& field : m_fields)
@@ -181,10 +205,13 @@ void CSVSink::addField(const std::string& name)
 
 void CSVSink::printHeader(std::ofstream& ofile)
 {
+
+  bool first = true;
+
   // Print out the tags
   for(const std::string& tag : m_tags)
     {
-      ofile << tag << ": ";
+      if (!(first == true)) ofile << ", ";
 
       if(m_tagsString.count(tag)>0)
 	ofile << m_tagsString[tag];
@@ -201,7 +228,8 @@ void CSVSink::printHeader(std::ofstream& ofile)
       if(m_tagsDouble.count(tag)>0)
 	ofile << m_tagsDouble[tag];
 
-      ofile << std::endl;
+      if (first == true) first = false;
+
     }
 
   // Print field names
-- 
GitLab


From 6b7849973e56d54e434f3d7fa8add4b16a815ef6 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Thu, 16 Jul 2020 15:10:38 -0700
Subject: [PATCH 04/23] check tags are same within measurement

---
 src/libDataSink/CSVSink.cpp | 3 ++-
 src/libDataSink/CSVSink.h   | 3 +++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index d8e45b23..fcb7fc0c 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -102,7 +102,7 @@ void CSVSink::setField(const std::string& name, double value)
 
 void CSVSink::recordPoint()
 {
-  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&!m_dirty;
+  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&(m_tags==m_lastTags)&&!m_dirty;
 
   std::ofstream ofile;
   ofile.open (m_directory+m_measurement+".csv", std::ios_base::app);
@@ -164,6 +164,7 @@ void CSVSink::recordPoint()
   // Update last configuration
   m_lastMeasurement=m_measurement;
   m_lastFields=m_fields;
+  m_lastTags = m_tags;
 
   m_finalSchema=true;
 }
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index 3a4de1cc..ec897157 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -102,6 +102,9 @@ private:
   // The last columns
   std::vector<std::string> m_lastFields;
 
+  // The last tags
+  std::vector<std::string> m_lastTags;
+
   //
   // State
 
-- 
GitLab


From 6cee4076fc34ececb76e5b79372f7e59df07eaf7 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Fri, 17 Jul 2020 12:02:58 -0700
Subject: [PATCH 05/23] update comments

---
 src/libDataSink/CSVSink.h | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index ec897157..a564c8f6 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -8,13 +8,13 @@
 
 //! \brief Data sink that saves to CSV files
 /**
- * When a data point is recorded, all fields are saved separated by a space
- * The header (tags and the column names) are printed when any of the following happens:
+ * When a data point is recorded, all fields are saved separated by comma
+ * The header (tags and the column names) are written to a file when any of the following happens:
  *  - The name of the measurement changes.
  *  - A tag value changes.
  *  - The set of fields changes.
  *
- * The header is not printed just because a previous measurement has been stopped. If
+ * The header is not written to file just because a previous measurement has been stopped. If
  * the field definitions and tags are the same, the previous header still applies.
  */
 class CSVSink : public IDataSink
@@ -46,8 +46,8 @@ public:
 
   //! \brief Print data point
   /**
-   * Prints the fields in the current data points. If the header
-   * needs to be reprinted, it is also done here.
+   * Saves the fields in the current data points in csv file. If the header
+   * needs to be written to file, it is also done here.
    */
   virtual void recordPoint();
   virtual void endMeasurement();
@@ -77,13 +77,10 @@ private:
    */
   void addField(const std::string& name);
 
-  //! \brief Print the header
+  //! \brief write the header
   /**
-   * Prints the header in the format.
-   *  Tag1: value
-   *  ...
-   *  Tagn: value
-   *  FIELD1 FIELD2 FIELD3
+   * write the header in the format.
+   *  Tag1 Tag 2 FIELD1 FIELD2 FIELD3
    *
    * \param ofile file to save the measurement
    */
-- 
GitLab


From 418953c7e745620148647b4b5280d02210e37c28 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 13:57:53 -0700
Subject: [PATCH 06/23] add timestamp,fix tag printing in header, add sink
 example in config

---
 src/configs/input-hw.json   |  6 ++-
 src/libDataSink/CSVSink.cpp | 74 ++++++++++++++++---------------------
 src/libDataSink/CSVSink.h   | 13 +++++--
 3 files changed, 47 insertions(+), 46 deletions(-)

diff --git a/src/configs/input-hw.json b/src/configs/input-hw.json
index 4f1ebafe..5ac1f503 100644
--- a/src/configs/input-hw.json
+++ b/src/configs/input-hw.json
@@ -6,7 +6,11 @@
     "datasinks":{
 	"Console":{
             "sinktype": "ConsoleSink"
-         }
+         },
+	 "File":{
+            "sinktype": "CSVSink",
+            "directory": "/home"
+        }
     },
     "devices": {
 	"PS" : {
diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index fcb7fc0c..5e904133 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -3,7 +3,6 @@
 #include <algorithm>
 #include <iostream>
 #include <iomanip>
-#include <fstream>
 
 #include "DataSinkRegistry.h"
 REGISTER_DATASINK(CSVSink)
@@ -68,6 +67,14 @@ void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time
   m_inMeasurement=true;
 
   m_measurement=measurement;
+
+  // Convert the timestamp to ms
+  m_timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch()).count();
+
+  // open CSV file
+  m_ofile.open (m_directory+"/"+m_measurement+".csv", std::ios_base::app);
+
+  
 }
 
 void CSVSink::setField(const std::string& name, const std::string& value)
@@ -104,38 +111,37 @@ void CSVSink::recordPoint()
 {
   bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&(m_tags==m_lastTags)&&!m_dirty;
 
-  std::ofstream ofile;
-  ofile.open (m_directory+m_measurement+".csv", std::ios_base::app);
-
   //
   // Print the header
   if(!same)
     { // First time recording point for measurement, print header
-      printHeader(ofile);
+      printHeader(m_ofile);
       m_dirty=false;
     }
+ 
+  // Print timestamp
+  m_ofile << m_timestamp;
 
-  bool first = true;
+  //
+  // Print the tags
   for(const std::string& tag : m_tags)
-    {
-      if (!(first == true)) ofile << ", ";
+    { 
+      m_ofile << ", ";
 
       if(m_tagsString.count(tag)>0)
-        ofile << m_tagsString[tag];
+	m_ofile << m_tagsString[tag];
 
       if(m_tagsInt8.count(tag)>0)
-        ofile << m_tagsInt8   [tag];
+	m_ofile << m_tagsInt8  [tag];
 
       if(m_tagsInt32.count(tag)>0)
-	ofile << m_tagsInt32   [tag];
+	m_ofile << m_tagsInt32 [tag];
 
       if(m_tagsInt64.count(tag)>0)
-        ofile << m_tagsInt64   [tag];
+        m_ofile << m_tagsInt64 [tag];
 
       if(m_tagsDouble.count(tag)>0)
-	ofile << m_tagsDouble[tag];
-
-      if (first == true) first = false;
+	m_ofile << m_tagsDouble[tag];
       
     }
 
@@ -144,23 +150,22 @@ void CSVSink::recordPoint()
   for(const std::string& field : m_fields)
     {
       if(m_fieldsString.count(field)>0)
-	ofile << "," << m_fieldsString[field];
+	m_ofile << "," << m_fieldsString[field];
 
       if(m_fieldsInt8.count(field)>0)
-	ofile << "," << m_fieldsInt8[field];
+	m_ofile << "," << m_fieldsInt8[field];
 
       if(m_fieldsInt32.count(field)>0)
-        ofile << "," << m_fieldsInt32[field];
+        m_ofile << "," << m_fieldsInt32[field];
 
       if(m_fieldsInt64.count(field)>0)
-        ofile << "," << m_fieldsInt64[field];
+        m_ofile << "," << m_fieldsInt64[field];
       
       if(m_fieldsDouble.count(field)>0)
-	ofile << "," << m_fieldsDouble[field];
+	m_ofile << "," << m_fieldsDouble[field];
     }
-  ofile << "\n";
+  m_ofile << std::endl;
 
-  ofile.close();
   // Update last configuration
   m_lastMeasurement=m_measurement;
   m_lastFields=m_fields;
@@ -172,6 +177,7 @@ void CSVSink::recordPoint()
 void CSVSink::endMeasurement()
 {
   m_inMeasurement=false;
+  m_ofile.close();
 }
 
 void CSVSink::checkTag(const std::string& name)
@@ -209,28 +215,12 @@ void CSVSink::printHeader(std::ofstream& ofile)
 
   bool first = true;
 
+  ofile << "Time";
+
   // Print out the tags
   for(const std::string& tag : m_tags)
     {
-      if (!(first == true)) ofile << ", ";
-
-      if(m_tagsString.count(tag)>0)
-	ofile << m_tagsString[tag];
-
-      if(m_tagsInt8.count(tag)>0)
-	ofile << m_tagsInt8   [tag];
-
-      if(m_tagsInt32.count(tag)>0)
-        ofile << m_tagsInt32   [tag];
-
-      if(m_tagsInt64.count(tag)>0)
-        ofile << m_tagsInt64   [tag];
-
-      if(m_tagsDouble.count(tag)>0)
-	ofile << m_tagsDouble[tag];
-
-      if (first == true) first = false;
-
+      ofile << "," << tag ;
     }
 
   // Print field names
@@ -238,5 +228,5 @@ void CSVSink::printHeader(std::ofstream& ofile)
     {
       ofile << "," << field;
     }
-  ofile << "\n";
+  ofile << std::endl;
 }
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index a564c8f6..99167695 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -3,12 +3,13 @@
 
 #include <vector>
 #include <unordered_map>
+#include <fstream>
 
 #include "IDataSink.h"
 
 //! \brief Data sink that saves to CSV files
 /**
- * When a data point is recorded, all fields are saved separated by comma
+ * When a data point is recorded, all fields are saved separated by a comma.
  * The header (tags and the column names) are written to a file when any of the following happens:
  *  - The name of the measurement changes.
  *  - A tag value changes.
@@ -44,10 +45,11 @@ public:
   virtual void setField(const std::string& name, int64_t value);
   virtual void setField(const std::string& name, double value);
 
-  //! \brief Print data point
+  //! \brief Save data point to CSV
   /**
    * Saves the fields in the current data points in csv file. If the header
    * needs to be written to file, it is also done here.
+   * The timestamp will be uploaded with milliseconds precision.
    */
   virtual void recordPoint();
   virtual void endMeasurement();
@@ -105,13 +107,18 @@ private:
   //
   // State
 
+  // timestamp
+  unsigned long long m_timestamp;
+
+  // Output CSV file
+  std::ofstream m_ofile;
+
   // Name of the current measurement (valid onyl if performing one)
   std::string m_measurement;
 
   // Currently performing a measurement
   bool m_inMeasurement=false;
 
-
   //
   // Data store for fields
   bool m_finalSchema=false;  // The measurement schema is locked
-- 
GitLab


From bb7d6e9ab69a276ff97b5b56b8f86a2881ebc3be Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 15:54:25 -0700
Subject: [PATCH 07/23] print header only first time file is created for the
 first measurement

---
 src/libDataSink/CSVSink.cpp | 23 ++++++++++++++---------
 src/libDataSink/CSVSink.h   |  8 +++++++-
 2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 5e904133..5913d621 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -28,7 +28,7 @@ void CSVSink::setTag(const std::string& name, const std::string& value)
   m_tagsString[name]=value;
 }
 
-void CSVSink::setTag(const std::string& name, int8_t value)
+     void CSVSink::setTag(const std::string& name, int8_t value)
 {
   checkTag(name);
   m_tagsInt8[name]=value;
@@ -71,10 +71,15 @@ void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time
   // Convert the timestamp to ms
   m_timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch()).count();
 
-  // open CSV file
-  m_ofile.open (m_directory+"/"+m_measurement+".csv", std::ios_base::app);
+  std::string fileName = m_directory+"/"+m_measurement+".csv";
 
-  
+  // Check if file exists
+  m_rfile.open(fileName);
+  m_fileExist = m_rfile.fail();
+  m_rfile.close();
+
+  // Open CSV file
+  m_ofile.open (fileName, std::ios_base::app);
 }
 
 void CSVSink::setField(const std::string& name, const std::string& value)
@@ -109,14 +114,15 @@ void CSVSink::setField(const std::string& name, double value)
 
 void CSVSink::recordPoint()
 {
-  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&(m_tags==m_lastTags)&&!m_dirty;
+  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&(m_tags==m_lastTags);
 
   //
   // Print the header
-  if(!same)
+  if(!same&&(m_fileExist&&m_first))
     { // First time recording point for measurement, print header
       printHeader(m_ofile);
       m_dirty=false;
+      m_first=false;
     }
  
   // Print timestamp
@@ -169,7 +175,7 @@ void CSVSink::recordPoint()
   // Update last configuration
   m_lastMeasurement=m_measurement;
   m_lastFields=m_fields;
-  m_lastTags = m_tags;
+  m_lastTags=m_tags;
 
   m_finalSchema=true;
 }
@@ -177,6 +183,7 @@ void CSVSink::recordPoint()
 void CSVSink::endMeasurement()
 {
   m_inMeasurement=false;
+  m_first=false;
   m_ofile.close();
 }
 
@@ -193,8 +200,6 @@ void CSVSink::checkTag(const std::string& name)
 
       m_tags.push_back(name);
     }
-
-  m_dirty=true;
 }
 
 void CSVSink::addField(const std::string& name)
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index 99167695..3f021431 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -119,6 +119,13 @@ private:
   // Currently performing a measurement
   bool m_inMeasurement=false;
 
+  // First time record point in data file
+  bool m_first = true;
+
+  // File exists
+  std::ifstream m_rfile;
+  bool m_fileExist;
+
   //
   // Data store for fields
   bool m_finalSchema=false;  // The measurement schema is locked
@@ -129,7 +136,6 @@ private:
   std::unordered_map<std::string, int64_t    > m_fieldsInt64;
   std::unordered_map<std::string, double     > m_fieldsDouble;
 
-
   //
   // Data store for tags
   bool m_dirty=true; // Tag values have changed, reprint
-- 
GitLab


From 3a05ab43a32d65c7242a11168a52263842e5d56c Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 15:58:54 -0700
Subject: [PATCH 08/23] change capitalization

---
 src/libDataSink/CSVSink.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 5913d621..f0bff3eb 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -220,7 +220,7 @@ void CSVSink::printHeader(std::ofstream& ofile)
 
   bool first = true;
 
-  ofile << "Time";
+  ofile << "time";
 
   // Print out the tags
   for(const std::string& tag : m_tags)
-- 
GitLab


From 3683c3b1412ffbf7bf0f50c6013fa96470600a65 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 16:00:42 -0700
Subject: [PATCH 09/23] update description

---
 src/libDataSink/CSVSink.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index 3f021431..2d80813c 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -7,7 +7,7 @@
 
 #include "IDataSink.h"
 
-//! \brief Data sink that saves to CSV files
+//! \brief Data sink that saves to CSV files with comma delimiter
 /**
  * When a data point is recorded, all fields are saved separated by a comma.
  * The header (tags and the column names) are written to a file when any of the following happens:
-- 
GitLab


From 8ece6022cd6e90038c6f4e2e9e62728bd371dd07 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 20:23:37 -0700
Subject: [PATCH 10/23] check if string contains comma

---
 src/libDataSink/CSVSink.cpp | 30 +++++++++++++++++++++++++-----
 src/libDataSink/CSVSink.h   | 13 +++++++++++++
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index f0bff3eb..2304b585 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -25,7 +25,7 @@ void CSVSink::setConfiguration(const nlohmann::json& config)
 void CSVSink::setTag(const std::string& name, const std::string& value)
 {
   checkTag(name);
-  m_tagsString[name]=value;
+  m_tagsString[name]=checkCSVEncoding(value);
 }
 
      void CSVSink::setTag(const std::string& name, int8_t value)
@@ -85,7 +85,7 @@ void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time
 void CSVSink::setField(const std::string& name, const std::string& value)
 {
   addField(name);
-  m_fieldsString[name]=value;
+  m_fieldsString[name]=checkCSVEncoding(value);
 }
 
 void CSVSink::setField(const std::string& name, int8_t value)
@@ -114,7 +114,7 @@ void CSVSink::setField(const std::string& name, double value)
 
 void CSVSink::recordPoint()
 {
-  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&(m_tags==m_lastTags);
+  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&(m_tags==m_lastTags)&&(!m_dirty);
 
   //
   // Print the header
@@ -187,8 +187,22 @@ void CSVSink::endMeasurement()
   m_ofile.close();
 }
 
+std::string CSVSink::checkCSVEncoding(const std::string& name)
+{
+  std::string newstr = name;
+
+  if (name.find(',') != std::string::npos)
+    {
+      newstr = "\""+name+"\"";
+    }
+  return newstr;      
+}
+
+
 void CSVSink::checkTag(const std::string& name)
 {
+  std::string newname; 
+
   if(checkReserved(name))
     throw std::runtime_error("Tag name \""+name+"\" is reserved.");
 
@@ -198,12 +212,16 @@ void CSVSink::checkTag(const std::string& name)
       if(m_inMeasurement)
 	throw std::runtime_error("CSVSink: Cannot change tag during measurement.");
 
-      m_tags.push_back(name);
+      newname = checkCSVEncoding(name);
+      m_tags.push_back(newname);
     }
+
+  m_dirty=true;
 }
 
 void CSVSink::addField(const std::string& name)
 {
+  std::string newname;
   if(checkReserved(name))
     throw std::runtime_error("Tag name \""+name+"\" is reserved.");
 
@@ -211,7 +229,9 @@ void CSVSink::addField(const std::string& name)
     {
       if(m_finalSchema)
 	throw std::runtime_error("CSVSink: Cannot add new fields after the first recordPoint.");
-      m_fields.push_back(name);
+
+      newname = checkCSVEncoding(name);
+      m_fields.push_back(newname);
     }
 }
 
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index 2d80813c..09908e47 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -55,6 +55,19 @@ public:
   virtual void endMeasurement();
 
 private:
+  //! \brief Check if string contains CSV character
+  /** 
+   * Check if the string contains a comma or double quote.
+   * 
+   * If the string contains a comma, return the string in double-quotes. 
+   *
+   * \param name string to be check.
+   *
+   * return encoded string if string contains a comma.
+   * else return original name.
+   */
+  std::string checkCSVEncoding(const std::string& name);
+
   //! \brief Checks whether a tag can be added
   /**
    * If tag is not defined, then it is added to the list.
-- 
GitLab


From 11a5a6dd592e541eaa266185f8c45ad8d56ae3b5 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 20:52:48 -0700
Subject: [PATCH 11/23] fix encoding

---
 src/libDataSink/CSVSink.cpp | 61 ++++++++++++++++++++-----------------
 1 file changed, 33 insertions(+), 28 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 2304b585..d21487da 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -24,32 +24,37 @@ void CSVSink::setConfiguration(const nlohmann::json& config)
 
 void CSVSink::setTag(const std::string& name, const std::string& value)
 {
-  checkTag(name);
-  m_tagsString[name]=checkCSVEncoding(value);
+  std::string newTag = checkCSVEncoding(name);
+  checkTag(newTag);
+  m_tagsString[newTag]=checkCSVEncoding(value);
 }
 
-     void CSVSink::setTag(const std::string& name, int8_t value)
+void CSVSink::setTag(const std::string& name, int8_t value)
 {
-  checkTag(name);
-  m_tagsInt8[name]=value;
+  std::string newTag = checkCSVEncoding(name);
+  checkTag(newTag);
+  m_tagsInt8[newTag]=value;
 }
 
 void CSVSink::setTag(const std::string& name, int32_t value)
 {
-  checkTag(name);
-  m_tagsInt32[name]=value;
+  std::string newTag = checkCSVEncoding(name);
+  checkTag(newTag);
+  m_tagsInt32[newTag]=value;
 }
 
 void CSVSink::setTag(const std::string& name, int64_t value)
 {
-  checkTag(name);
-  m_tagsInt64[name]=value;
+  std::string newTag = checkCSVEncoding(name);
+  checkTag(newTag);
+  m_tagsInt64[newTag]=value;
 }
 
 void CSVSink::setTag(const std::string& name, double value)
 {
-  checkTag(name);
-  m_tagsDouble[name]=value;
+  std::string newTag = checkCSVEncoding(name);
+  checkTag(newTag);
+  m_tagsDouble[newTag]=value;
 }
 
 void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time_point<std::chrono::system_clock> time)
@@ -84,32 +89,37 @@ void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time
 
 void CSVSink::setField(const std::string& name, const std::string& value)
 {
-  addField(name);
-  m_fieldsString[name]=checkCSVEncoding(value);
+  std::string newField = checkCSVEncoding(name);
+  addField(newField);
+  m_fieldsString[newField]=checkCSVEncoding(value);
 }
 
 void CSVSink::setField(const std::string& name, int8_t value)
 {
-  addField(name);
-  m_fieldsInt8[name]=value;
+  std::string newField = checkCSVEncoding(name);
+  addField(newField);
+  m_fieldsInt8[newField]=value;
 }
 
 void CSVSink::setField(const std::string& name, int32_t value)
 {
-  addField(name);
-  m_fieldsInt32[name]=value;
+  std::string newField = checkCSVEncoding(name);
+  addField(newField);
+  m_fieldsInt32[newField]=value;
 }
 
 void CSVSink::setField(const std::string& name, int64_t value)
 {
-  addField(name);
-  m_fieldsInt64[name]=value;
+  std::string newField = checkCSVEncoding(name);
+  addField(newField);
+  m_fieldsInt64[newField]=value;
 }
 
 void CSVSink::setField(const std::string& name, double value)
 {
-  addField(name);
-  m_fieldsDouble[name]=value;
+  std::string newField = checkCSVEncoding(name);
+  addField(newField);
+  m_fieldsDouble[newField]=value;
 }
 
 void CSVSink::recordPoint()
@@ -201,8 +211,6 @@ std::string CSVSink::checkCSVEncoding(const std::string& name)
 
 void CSVSink::checkTag(const std::string& name)
 {
-  std::string newname; 
-
   if(checkReserved(name))
     throw std::runtime_error("Tag name \""+name+"\" is reserved.");
 
@@ -212,8 +220,7 @@ void CSVSink::checkTag(const std::string& name)
       if(m_inMeasurement)
 	throw std::runtime_error("CSVSink: Cannot change tag during measurement.");
 
-      newname = checkCSVEncoding(name);
-      m_tags.push_back(newname);
+      m_tags.push_back(name);
     }
 
   m_dirty=true;
@@ -221,7 +228,6 @@ void CSVSink::checkTag(const std::string& name)
 
 void CSVSink::addField(const std::string& name)
 {
-  std::string newname;
   if(checkReserved(name))
     throw std::runtime_error("Tag name \""+name+"\" is reserved.");
 
@@ -230,8 +236,7 @@ void CSVSink::addField(const std::string& name)
       if(m_finalSchema)
 	throw std::runtime_error("CSVSink: Cannot add new fields after the first recordPoint.");
 
-      newname = checkCSVEncoding(name);
-      m_fields.push_back(newname);
+      m_fields.push_back(name);
     }
 }
 
-- 
GitLab


From ac89036eaf0e8a05ff658b115e09a0d83eeaed9e Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 20:57:39 -0700
Subject: [PATCH 12/23] change rfile declaration

---
 src/libDataSink/CSVSink.cpp | 7 ++++---
 src/libDataSink/CSVSink.h   | 1 -
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index d21487da..68fb6026 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -79,9 +79,10 @@ void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time
   std::string fileName = m_directory+"/"+m_measurement+".csv";
 
   // Check if file exists
-  m_rfile.open(fileName);
-  m_fileExist = m_rfile.fail();
-  m_rfile.close();
+  std::ifstream rfile;
+  rfile.open(fileName);
+  m_fileExist = rfile.fail();
+  rfile.close();
 
   // Open CSV file
   m_ofile.open (fileName, std::ios_base::app);
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index 09908e47..a2686a06 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -136,7 +136,6 @@ private:
   bool m_first = true;
 
   // File exists
-  std::ifstream m_rfile;
   bool m_fileExist;
 
   //
-- 
GitLab


From 1ec17879c4fef3720ec49237c6ba69130da06f0f Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 21:04:03 -0700
Subject: [PATCH 13/23] check if output file opens

---
 src/libDataSink/CSVSink.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 68fb6026..4e8849b4 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -86,6 +86,8 @@ void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time
 
   // Open CSV file
   m_ofile.open (fileName, std::ios_base::app);
+  if (!(m_ofile.is_open()))
+    throw std::runtime_error("Could not open file: "+fileName);
 }
 
 void CSVSink::setField(const std::string& name, const std::string& value)
-- 
GitLab


From 8f9e231ae929fc4bf51ff03f0bc19ce5b259d311 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 21:07:35 -0700
Subject: [PATCH 14/23] remove unnecessary m_first

---
 src/libDataSink/CSVSink.cpp | 4 +---
 src/libDataSink/CSVSink.h   | 3 ---
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 4e8849b4..00e53009 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -131,11 +131,10 @@ void CSVSink::recordPoint()
 
   //
   // Print the header
-  if(!same&&(m_fileExist&&m_first))
+  if(!same&&(m_fileExist))
     { // First time recording point for measurement, print header
       printHeader(m_ofile);
       m_dirty=false;
-      m_first=false;
     }
  
   // Print timestamp
@@ -196,7 +195,6 @@ void CSVSink::recordPoint()
 void CSVSink::endMeasurement()
 {
   m_inMeasurement=false;
-  m_first=false;
   m_ofile.close();
 }
 
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index a2686a06..68526eb6 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -132,9 +132,6 @@ private:
   // Currently performing a measurement
   bool m_inMeasurement=false;
 
-  // First time record point in data file
-  bool m_first = true;
-
   // File exists
   bool m_fileExist;
 
-- 
GitLab


From dc87785d3ee948da333df44b39201023e1a2a51b Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 21:11:33 -0700
Subject: [PATCH 15/23] change when to print header

---
 src/libDataSink/CSVSink.cpp | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 00e53009..9c1c9f12 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -81,7 +81,7 @@ void CSVSink::startMeasurement(const std::string& measurement, std::chrono::time
   // Check if file exists
   std::ifstream rfile;
   rfile.open(fileName);
-  m_fileExist = rfile.fail();
+  m_fileExist = !(rfile.fail());
   rfile.close();
 
   // Open CSV file
@@ -127,12 +127,10 @@ void CSVSink::setField(const std::string& name, double value)
 
 void CSVSink::recordPoint()
 {
-  bool same=(m_measurement==m_lastMeasurement)&&(m_fields==m_lastFields)&&(m_tags==m_lastTags)&&(!m_dirty);
-
   //
   // Print the header
-  if(!same&&(m_fileExist))
-    { // First time recording point for measurement, print header
+  if(!(m_fileExist))
+    { // First time recording point in CSV file, print header
       printHeader(m_ofile);
       m_dirty=false;
     }
-- 
GitLab


From c3ff837f2925e39544dcdcd8aa94afd5e4cfdbbb Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 21:16:37 -0700
Subject: [PATCH 16/23] update documentation and remove m_dirty

---
 src/libDataSink/CSVSink.cpp |  2 --
 src/libDataSink/CSVSink.h   | 11 ++---------
 2 files changed, 2 insertions(+), 11 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 9c1c9f12..2efedc33 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -132,7 +132,6 @@ void CSVSink::recordPoint()
   if(!(m_fileExist))
     { // First time recording point in CSV file, print header
       printHeader(m_ofile);
-      m_dirty=false;
     }
  
   // Print timestamp
@@ -222,7 +221,6 @@ void CSVSink::checkTag(const std::string& name)
       m_tags.push_back(name);
     }
 
-  m_dirty=true;
 }
 
 void CSVSink::addField(const std::string& name)
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index 68526eb6..b7fa6e74 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -9,14 +9,8 @@
 
 //! \brief Data sink that saves to CSV files with comma delimiter
 /**
- * When a data point is recorded, all fields are saved separated by a comma.
- * The header (tags and the column names) are written to a file when any of the following happens:
- *  - The name of the measurement changes.
- *  - A tag value changes.
- *  - The set of fields changes.
- *
- * The header is not written to file just because a previous measurement has been stopped. If
- * the field definitions and tags are the same, the previous header still applies.
+ * When a data point is recorded, a timestamp followed by tags and fields are saved separated by a comma.
+ * The header (tags and field names) are written to a file when a file is first created.
  */
 class CSVSink : public IDataSink
 {
@@ -147,7 +141,6 @@ private:
 
   //
   // Data store for tags
-  bool m_dirty=true; // Tag values have changed, reprint
   std::vector<std::string> m_tags;
   std::unordered_map<std::string, std::string> m_tagsString;
   std::unordered_map<std::string, int8_t     > m_tagsInt8 ;
-- 
GitLab


From 144aac6e1a49cd8cfc8522072c9fb4f4d52be243 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 21:27:01 -0700
Subject: [PATCH 17/23] update format

---
 src/libDataSink/CSVSink.cpp | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 2efedc33..6db84f76 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -141,7 +141,7 @@ void CSVSink::recordPoint()
   // Print the tags
   for(const std::string& tag : m_tags)
     { 
-      m_ofile << ", ";
+      m_ofile << ",";
 
       if(m_tagsString.count(tag)>0)
 	m_ofile << m_tagsString[tag];
@@ -164,20 +164,22 @@ void CSVSink::recordPoint()
   // Print the fields
   for(const std::string& field : m_fields)
     {
+      m_ofile << ","; 
+
       if(m_fieldsString.count(field)>0)
-	m_ofile << "," << m_fieldsString[field];
+	m_ofile << m_fieldsString[field];
 
       if(m_fieldsInt8.count(field)>0)
-	m_ofile << "," << m_fieldsInt8[field];
+	m_ofile << m_fieldsInt8[field];
 
       if(m_fieldsInt32.count(field)>0)
-        m_ofile << "," << m_fieldsInt32[field];
+        m_ofile << m_fieldsInt32[field];
 
       if(m_fieldsInt64.count(field)>0)
-        m_ofile << "," << m_fieldsInt64[field];
+        m_ofile << m_fieldsInt64[field];
       
       if(m_fieldsDouble.count(field)>0)
-	m_ofile << "," << m_fieldsDouble[field];
+	m_ofile << m_fieldsDouble[field];
     }
   m_ofile << std::endl;
 
-- 
GitLab


From 0287076d5dc24a5811dc1009ba5e463276e58885 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 21:38:06 -0700
Subject: [PATCH 18/23] update documentation

---
 src/libDataSink/CSVSink.h | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index b7fa6e74..8a1518d9 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -9,8 +9,12 @@
 
 //! \brief Data sink that saves to CSV files with comma delimiter
 /**
- * When a data point is recorded, a timestamp followed by tags and fields are saved separated by a comma.
- * The header (tags and field names) are written to a file when a file is first created.
+ * When a data point is recorded, the following are saved in this order, separated by a comma:
+ * - timestamp (under column heading time)
+ * - tags 
+ * - fields
+ *
+ * The header (time, tag names, and field names) are written to a file when a file is first created.
  */
 class CSVSink : public IDataSink
 {
-- 
GitLab


From 41a4f6f3d6cc762d14287dda52aa38a874bc0e94 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 21:46:26 -0700
Subject: [PATCH 19/23] remove unused variable

---
 src/libDataSink/CSVSink.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 6db84f76..1a72a691 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -241,9 +241,6 @@ void CSVSink::addField(const std::string& name)
 
 void CSVSink::printHeader(std::ofstream& ofile)
 {
-
-  bool first = true;
-
   ofile << "time";
 
   // Print out the tags
-- 
GitLab


From b23bb92fe9547f956964f9e37b3c0363bd85a27c Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Mon, 27 Jul 2020 22:08:44 -0700
Subject: [PATCH 20/23] update csv encoding

---
 src/libDataSink/CSVSink.cpp | 14 +++++++++++++-
 src/libDataSink/CSVSink.h   |  1 +
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 1a72a691..eb706649 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -201,9 +201,21 @@ std::string CSVSink::checkCSVEncoding(const std::string& name)
 {
   std::string newstr = name;
 
+  if(name.find("\"") != std::string::npos)
+    {
+      size_t start_pos = 0;
+      std::string sold = "\"";
+      std::string snew = "\"\"";
+      
+      while((start_pos = newstr.find(sold, start_pos)) != std::string::npos) {
+        newstr.replace(start_pos, sold.length(), snew);
+        start_pos += snew.length(); 
+      }
+    }
+
   if (name.find(',') != std::string::npos)
     {
-      newstr = "\""+name+"\"";
+      newstr = "\""+newstr+"\"";
     }
   return newstr;      
 }
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index 8a1518d9..c5627032 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -58,6 +58,7 @@ private:
    * Check if the string contains a comma or double quote.
    * 
    * If the string contains a comma, return the string in double-quotes. 
+   * If the string contains quotes, the quotation mark will be escaped.
    *
    * \param name string to be check.
    *
-- 
GitLab


From 6bf9411c38a9f61642803745759871ba48916024 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Tue, 28 Jul 2020 06:28:52 -0700
Subject: [PATCH 21/23] remove unusued variables, updated config, update
 encoding

---
 src/configs/input-hw.json   |  2 +-
 src/libDataSink/CSVSink.cpp | 28 ++++++++++++----------------
 src/libDataSink/CSVSink.h   | 14 +-------------
 3 files changed, 14 insertions(+), 30 deletions(-)

diff --git a/src/configs/input-hw.json b/src/configs/input-hw.json
index 5ac1f503..6ec89242 100644
--- a/src/configs/input-hw.json
+++ b/src/configs/input-hw.json
@@ -9,7 +9,7 @@
          },
 	 "File":{
             "sinktype": "CSVSink",
-            "directory": "/home"
+            "directory": "myOutputData"
         }
     },
     "devices": {
diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index eb706649..7434d18e 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -183,11 +183,6 @@ void CSVSink::recordPoint()
     }
   m_ofile << std::endl;
 
-  // Update last configuration
-  m_lastMeasurement=m_measurement;
-  m_lastFields=m_fields;
-  m_lastTags=m_tags;
-
   m_finalSchema=true;
 }
 
@@ -201,20 +196,21 @@ std::string CSVSink::checkCSVEncoding(const std::string& name)
 {
   std::string newstr = name;
 
-  if(name.find("\"") != std::string::npos)
+  if (name.find(',') != std::string::npos)
     {
-      size_t start_pos = 0;
-      std::string sold = "\"";
-      std::string snew = "\"\"";
       
-      while((start_pos = newstr.find(sold, start_pos)) != std::string::npos) {
-        newstr.replace(start_pos, sold.length(), snew);
-        start_pos += snew.length(); 
-      }
-    }
+      if(name.find("\"") != std::string::npos)
+	{
+	  size_t start_pos = 0;
+	  std::string sold = "\"";
+	  std::string snew = "\"\"";
+
+	  while((start_pos = newstr.find(sold, start_pos)) != std::string::npos) {
+	    newstr.replace(start_pos, sold.length(), snew);
+	    start_pos += snew.length();
+	  }
+	}
 
-  if (name.find(',') != std::string::npos)
-    {
       newstr = "\""+newstr+"\"";
     }
   return newstr;      
diff --git a/src/libDataSink/CSVSink.h b/src/libDataSink/CSVSink.h
index c5627032..fe795cfc 100644
--- a/src/libDataSink/CSVSink.h
+++ b/src/libDataSink/CSVSink.h
@@ -58,7 +58,7 @@ private:
    * Check if the string contains a comma or double quote.
    * 
    * If the string contains a comma, return the string in double-quotes. 
-   * If the string contains quotes, the quotation mark will be escaped.
+   * If the string contains quotes and a comma, the quotation mark will be escaped.
    *
    * \param name string to be check.
    *
@@ -104,18 +104,6 @@ private:
   // Directory where files are saved
   std::string m_directory;
 
-  //
-  // The last state (before endMeasurement)
-
-  // Name of the last measurement
-  std::string m_lastMeasurement;
-
-  // The last columns
-  std::vector<std::string> m_lastFields;
-
-  // The last tags
-  std::vector<std::string> m_lastTags;
-
   //
   // State
 
-- 
GitLab


From a06b6824bcf628ea405ba38cc3492d924c191793 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Tue, 28 Jul 2020 07:57:06 -0700
Subject: [PATCH 22/23] remove unnecessary if and add comments

---
 src/libDataSink/CSVSink.cpp | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index 7434d18e..fdb9c413 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -196,21 +196,20 @@ std::string CSVSink::checkCSVEncoding(const std::string& name)
 {
   std::string newstr = name;
 
+  // check if the name contains a comma
   if (name.find(',') != std::string::npos)
     {
-      
-      if(name.find("\"") != std::string::npos)
-	{
-	  size_t start_pos = 0;
-	  std::string sold = "\"";
-	  std::string snew = "\"\"";
-
-	  while((start_pos = newstr.find(sold, start_pos)) != std::string::npos) {
-	    newstr.replace(start_pos, sold.length(), snew);
-	    start_pos += snew.length();
-	  }
-	}
+      size_t start_pos = 0;
+      std::string sold = "\"";
+      std::string snew = "\"\"";
+
+      // check if string has quotation mark, if so replace by double quotation marks
+      while((start_pos = newstr.find(sold, start_pos)) != std::string::npos) {
+	newstr.replace(start_pos, sold.length(), snew);
+	start_pos += snew.length();
+      }
 
+      // enclose the string in double quotes
       newstr = "\""+newstr+"\"";
     }
   return newstr;      
-- 
GitLab


From 7bef1f33ad230f33b89ebfefcfe9d9ac4282e2f3 Mon Sep 17 00:00:00 2001
From: Elodie Resseguie <elodie.deborah.resseguie@cern.ch>
Date: Tue, 28 Jul 2020 07:58:58 -0700
Subject: [PATCH 23/23] change string type to static const

---
 src/libDataSink/CSVSink.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libDataSink/CSVSink.cpp b/src/libDataSink/CSVSink.cpp
index fdb9c413..414ab539 100644
--- a/src/libDataSink/CSVSink.cpp
+++ b/src/libDataSink/CSVSink.cpp
@@ -200,8 +200,8 @@ std::string CSVSink::checkCSVEncoding(const std::string& name)
   if (name.find(',') != std::string::npos)
     {
       size_t start_pos = 0;
-      std::string sold = "\"";
-      std::string snew = "\"\"";
+      static const std::string sold = "\"";
+      static const std::string snew = "\"\"";
 
       // check if string has quotation mark, if so replace by double quotation marks
       while((start_pos = newstr.find(sold, start_pos)) != std::string::npos) {
-- 
GitLab