From 2a9f9ef6ad325942caa03d1c06894576f786534e Mon Sep 17 00:00:00 2001 From: John Madden Date: Sun, 30 Mar 2025 12:55:40 -0700 Subject: [PATCH 1/2] Added proof of concept --- src/CUcontent_MBsSWs.cpp | 41 ++++++++++++++ src/CUcontent_MBsSWs.h | 5 ++ src/csvfile.h | 112 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 src/csvfile.h diff --git a/src/CUcontent_MBsSWs.cpp b/src/CUcontent_MBsSWs.cpp index b048f78e..3754ec94 100644 --- a/src/CUcontent_MBsSWs.cpp +++ b/src/CUcontent_MBsSWs.cpp @@ -363,6 +363,38 @@ bool CUcontent_MBsSWs::startMBSWreading() } else goto err; + + // create csv object and write header + csv = new csvfile("test.csv"); + + // write header + for (size_t i=0; i < _MBSWmetaList.size(); i++) + { + const MBSWmetadata_dt& metadata = _MBSWmetaList.at(i); + + std::string title; + std::string unit; + + // find title and units + if (metadata.blockType == BlockType::MB) + { + const mb_dt& mb = _supportedMBs.at(metadata.nativeIndex); + title = mb.title.toStdString(); + unit = mb.unit.toStdString(); + } + else if (metadata.blockType == BlockType::SW) + { + const sw_dt& sw = _supportedSWs.at(metadata.nativeIndex); + title = sw.title.toStdString(); + unit = sw.unit.toStdString(); + } + + // write to file + *csv << title + " (" + unit + ")"; + + } + *csv << endrow; + // Reset old data: _lastValues.clear(); _minmaxData.clear(); @@ -410,6 +442,8 @@ bool CUcontent_MBsSWs::stopMBSWreading() } } disconnect( _SSMPdev, SIGNAL( newMBSWrawValues(const std::vector&, int) ), this, SLOT( processMBSWRawValues(const std::vector&, int) ) ); + // close csv files (also flushes csv) + delete csv; // Set text+icon of start/stop-button: labelStartStopButtonReadyForStart(); // Enable delete button if MBs/SWs are selected on the table: @@ -711,6 +745,13 @@ void CUcontent_MBsSWs::processMBSWRawValues(const std::vector& raw _valuesTableView->updateMBSWvalues(valueStrList, minValueStrList, maxValueStrList, unitStrList); // Output refresh duration: updateTimeInfo(refreshduration_ms); + + // write to csv + for (size_t i=0; i < valueStrList.size(); i++) { + std::string value = valueStrList.at(i).toStdString(); + *csv << value; + } + *csv << endrow; } diff --git a/src/CUcontent_MBsSWs.h b/src/CUcontent_MBsSWs.h index 67cd36d8..b63badf7 100644 --- a/src/CUcontent_MBsSWs.h +++ b/src/CUcontent_MBsSWs.h @@ -40,6 +40,8 @@ // To do binary file operations storing the current setup #include +#include "csvfile.h" + class MBSWvalue_dt { public: @@ -106,6 +108,9 @@ class CUcontent_MBsSWs : public QWidget, private Ui::MBSWcontent_Form std::vector _tableRowPosIndexes; /* index of the row at which the MB/SW is displayed in the values-table-widget */ bool _MBSWreading; + // pointer to csv file object + csvfile *csv = nullptr; + void setupTimeModeUiElements(); bool validateMBSWselection(const std::vector& MBSWmetaList); void setMBSWselectionUnvalidated(const std::vector& MBSWmetaList); diff --git a/src/csvfile.h b/src/csvfile.h new file mode 100644 index 00000000..d39d5b73 --- /dev/null +++ b/src/csvfile.h @@ -0,0 +1,112 @@ +#pragma once +#include +#include +#include +#include + +class csvfile; + +inline static csvfile& endrow(csvfile& file); +inline static csvfile& flush(csvfile& file); + +class csvfile +{ + std::ofstream fs_; + bool is_first_; + const std::string separator_; + const std::string escape_seq_; + const std::string special_chars_; +public: + csvfile(const std::string filename, const std::string separator = ";") + : fs_() + , is_first_(true) + , separator_(separator) + , escape_seq_("\"") + , special_chars_("\"") + { + fs_.exceptions(std::ios::failbit | std::ios::badbit); + fs_.open(filename); + } + + ~csvfile() + { + flush(); + fs_.close(); + } + + void flush() + { + fs_.flush(); + } + + void endrow() + { + fs_ << std::endl; + is_first_ = true; + } + + csvfile& operator << ( csvfile& (* val)(csvfile&)) + { + return val(*this); + } + + csvfile& operator << (const char * val) + { + return write(escape(val)); + } + + csvfile& operator << (const std::string & val) + { + return write(escape(val)); + } + + template + csvfile& operator << (const T& val) + { + return write(val); + } + +private: + template + csvfile& write (const T& val) + { + if (!is_first_) + { + fs_ << separator_; + } + else + { + is_first_ = false; + } + fs_ << val; + return *this; + } + + std::string escape(const std::string & val) + { + std::ostringstream result; + result << '"'; + std::string::size_type to, from = 0u, len = val.length(); + while (from < len && + std::string::npos != (to = val.find_first_of(special_chars_, from))) + { + result << val.substr(from, to - from) << escape_seq_ << val[to]; + from = to + 1; + } + result << val.substr(from) << '"'; + return result.str(); + } +}; + + +inline static csvfile& endrow(csvfile& file) +{ + file.endrow(); + return file; +} + +inline static csvfile& flush(csvfile& file) +{ + file.flush(); + return file; +} From 3e7af9abdf822539655b5a8af9ed0221246af5fd Mon Sep 17 00:00:00 2001 From: John Madden Date: Mon, 31 Mar 2025 08:44:13 -0700 Subject: [PATCH 2/2] Added timestamp column to csv --- src/CUcontent_MBsSWs.cpp | 26 +++++++++++++++++++++++++- src/CUcontent_MBsSWs.h | 4 ++++ src/csvfile.h | 18 +++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/CUcontent_MBsSWs.cpp b/src/CUcontent_MBsSWs.cpp index 3754ec94..d83b17fb 100644 --- a/src/CUcontent_MBsSWs.cpp +++ b/src/CUcontent_MBsSWs.cpp @@ -364,8 +364,17 @@ bool CUcontent_MBsSWs::startMBSWreading() else goto err; + // store start time + startTime = std::chrono::high_resolution_clock::now(); + // create csv object and write header - csv = new csvfile("test.csv"); + csv = new csvfile("measurements.csv"); + + // global time + *csv << "Time"; + + // time since measurement + *csv << "Delta Time"; // write header for (size_t i=0; i < _MBSWmetaList.size(); i++) @@ -747,11 +756,26 @@ void CUcontent_MBsSWs::processMBSWRawValues(const std::vector& raw updateTimeInfo(refreshduration_ms); // write to csv + + // get timestamp + const auto currTime = std::chrono::high_resolution_clock::now(); + const std::chrono::duration deltaTime = currTime - startTime; + + // conver to unix epoch time in ms + const double unixEpochMs = std::chrono::duration_cast + (currTime.time_since_epoch()).count() / 1000.0; + const double deltaEpochMs = std::chrono::duration_cast + (deltaTime).count() / 1000.0; + + // save to csv + *csv << std::fixed << unixEpochMs; + *csv << deltaEpochMs; for (size_t i=0; i < valueStrList.size(); i++) { std::string value = valueStrList.at(i).toStdString(); *csv << value; } *csv << endrow; + csv->flush(); } diff --git a/src/CUcontent_MBsSWs.h b/src/CUcontent_MBsSWs.h index b63badf7..870494fb 100644 --- a/src/CUcontent_MBsSWs.h +++ b/src/CUcontent_MBsSWs.h @@ -40,6 +40,8 @@ // To do binary file operations storing the current setup #include +#include + #include "csvfile.h" class MBSWvalue_dt @@ -110,6 +112,8 @@ class CUcontent_MBsSWs : public QWidget, private Ui::MBSWcontent_Form // pointer to csv file object csvfile *csv = nullptr; + // measurement start time (default ot current time) + std::chrono::high_resolution_clock::time_point startTime; void setupTimeModeUiElements(); bool validateMBSWselection(const std::vector& MBSWmetaList); diff --git a/src/csvfile.h b/src/csvfile.h index d39d5b73..e36561c9 100644 --- a/src/csvfile.h +++ b/src/csvfile.h @@ -1,4 +1,12 @@ -#pragma once +/** + * csvfile.h - Utility for writing CSV files + * + * Based on this github gist: https://gist.github.com/rudolfovich/f250900f1a833e715260a66c87369d15 + */ + +#ifndef CSVFILE_H +#define CSVFILE_H + #include #include #include @@ -60,6 +68,12 @@ class csvfile return write(escape(val)); } + csvfile& operator<<(std::ios_base& (*manip)(std::ios_base&)) + { + fs_ << manip; + return *this; + } + template csvfile& operator << (const T& val) { @@ -110,3 +124,5 @@ inline static csvfile& flush(csvfile& file) file.flush(); return file; } + +#endif