Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration of the FFT Analysis into OmnAIView #182

Merged
merged 45 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ffeff18
Diagnose Menu reopened, changed the language settings to FFT, FFT ana…
AKMaily Oct 2, 2024
714828c
Initial StateManager for the Analyze Menu, for cleaner code and easie…
AKMaily Oct 2, 2024
f8929de
Minimal Statemanagement
AKMaily Oct 2, 2024
a1213f3
Selected Current Data and select one device, IN TESTING
AKMaily Oct 2, 2024
ff3d125
Select a file or a device
AKMaily Oct 2, 2024
0115666
Correct state management for selecting datapath or device, IN TESTING
AKMaily Oct 2, 2024
e5a69c1
Code Improvements, IN TESTING
AKMaily Oct 3, 2024
74a6c7a
Private instead of global variables. Improved code design
AKMaily Oct 3, 2024
81eb600
temporary save
AKMaily Oct 3, 2024
e914882
Loading and Sending implemented, found a bug with files that are csv …
AKMaily Oct 3, 2024
ff62448
Getting analyze response, IN TESTING
AKMaily Oct 4, 2024
e2508ef
Debugging
AKMaily Oct 4, 2024
f965147
Debugging
AKMaily Oct 4, 2024
01f8697
Trying to fix loading current Data
AKMaily Oct 4, 2024
7f88f67
Typo
AKMaily Oct 4, 2024
be7328a
In testin
AKMaily Oct 4, 2024
7486d5a
TESTING, loading data from current device still buggy
AKMaily Oct 6, 2024
3d16d80
Bug fixed
AKMaily Oct 6, 2024
f3fb03d
First step: Loading data in a general .csv format into the application
AKMaily Oct 7, 2024
ba6f621
API Response saved as .csv instead of JSON
AKMaily Oct 7, 2024
7f38b54
Debugging: Loading a general Analysis File
AKMaily Oct 7, 2024
1613268
Loading analyzed Files Debugging 2
AKMaily Oct 8, 2024
9ef7fa4
Loading Analysed data into the plot, IN TESTING
AKMaily Oct 10, 2024
2a3906e
Test1: Loading analysis data into plot
AKMaily Oct 10, 2024
ed711dd
Test2: Loading analysis data into plot
AKMaily Oct 10, 2024
b613f59
Test1: Loading analysis data into plot
AKMaily Oct 10, 2024
eacaace
Test2: Loading analysis data into plot
AKMaily Oct 10, 2024
5cbb6f9
Test 3: Analysis data in plot region
AKMaily Oct 10, 2024
b3680a1
Test 4: Loading analysis data into plot
AKMaily Oct 10, 2024
a21591b
Reset function and histogram plot for fft
AKMaily Oct 10, 2024
deac975
Histogram size test
AKMaily Oct 10, 2024
874ec0f
Plot from 65.000 datapoints can now present up to 4.000.000.000 datap…
AKMaily Oct 10, 2024
5d0799d
Analysis reset by clicking reset or search devices, analysis bug fix
AKMaily Oct 10, 2024
0c6bea5
Reverse Changes using different way to show FFT response
AKMaily Oct 16, 2024
f2c192c
IN TESTING: Last API_FFT Changes for loading the response
AKMaily Oct 16, 2024
05eb8ba
IN TESTING: Plot shown
AKMaily Oct 16, 2024
fd27f43
Fixing: Software closes while trying to load analysis
AKMaily Oct 16, 2024
e5f0297
Testing: Int to double
AKMaily Oct 16, 2024
b7e70f6
Analyse Menu with correct Units, Versionstring, Color changed
AKMaily Oct 16, 2024
84261ec
Analysed menu finished as well as some bug fixes
AKMaily Oct 16, 2024
2f5be98
Fix FFT Name
AKMaily Oct 16, 2024
aebafcb
Legend in color of Histogram
AKMaily Oct 16, 2024
7c36a04
No log
AKMaily Oct 16, 2024
c01a0e7
No log in legend
AKMaily Oct 16, 2024
d45026d
Changed Linestyle
AKMaily Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ add_executable(OmniView
src/saves_popup.cpp
src/style.cpp
src/handler.cpp
src/analyze_data.cpp
)

target_add_default_build_options(OmniView PUBLIC)
Expand Down
328 changes: 328 additions & 0 deletions src/analyze_data.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
#include "analyze_data.hpp"

fs::path generate_analyze_menu( // generate the whole menu: Startpoint
bool &open_analyze_menu,bool &LOADANALYSISDATA,
const std::map<Omniscope::Id, std::vector<std::pair<double, double>>>
&captureData) {

static AnalyzeStateManager stateManager; // Initialize AnalyzeStateManager

// generate the PopUpMenu
ImGui::OpenPopup(appLanguage[Key::AnalyzeData]);

if (ImGui::BeginPopupModal(appLanguage[Key::AnalyzeData], nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {

// Frame
if(stateManager.getState() == State::RESET){
stateManager.reset();
}
ImGui::Text(appLanguage[Key::AnalyzeData]);
stateManager.selectDataType(open_analyze_menu, captureData); // select if Data should be loaded from File or from a current measurement
stateManager.selectData(captureData); //select which device or which file the data should be loaded from
stateManager.setMetaData(); // here there could be an array that is requested before from the API that loads the correct metadata files, currently no MetaData needed

// Close PopUp
if (ImGui::Button(appLanguage[Key::Back])) {
stateManager.reset();
open_analyze_menu = false;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
// Send Data
if(ImGui::Button(appLanguage[Key::Send])){
stateManager.loadAndSendData(captureData); // One function because of the loading bar
}
if(stateManager.getState() == State::DATAISSENDING){ // waiting for API response
stateManager.whileSendingProcess();
}
if(stateManager.getState() == State::DATAWASSEND){
ImGui::OpenPopup(appLanguage[Key::Data_upload],
ImGuiPopupFlags_NoOpenOverExistingPopup);
stateManager.generateAnalyzeAnswerPopUp(LOADANALYSISDATA);
}
}
ImGui::EndPopup();
fs::path FilePath = stateManager.GetFilePath();
return FilePath;
}

void AnalyzeStateManager::setState(State state) {
this->currentState = state;
}

State AnalyzeStateManager::getState() { return this->currentState; }

void AnalyzeStateManager::selectDataType(
bool &open_analyze_menu,
const std::map<Omniscope::Id, std::vector<std::pair<double, double>>>
&captureData) {

// Select Current Wave
if (ImGui::RadioButton(appLanguage[Key::Usr_curnt_wave],
radioButtonCurrentData)) {
radioButtonCurrentData = !radioButtonCurrentData;
radioButtonFileData = false;
if (!captureData.size()) { // Check for current measurement, else error Popup
ImGui::OpenPopup(appLanguage[Key::WvForms_warning],
ImGuiPopupFlags_NoOpenOverExistingPopup);
radioButtonCurrentData = false;
currentState = State::NODATA;
} else if (radioButtonCurrentData)
{
currentState = State::CURRENTDATAWANTED;
}
else currentState = State::NODATA;
}

ImGui::SeparatorText(" ");

// OR select a File from Browser
if (ImGui::RadioButton(appLanguage[Key::Wv_from_file], radioButtonFileData)){
radioButtonFileData = !radioButtonFileData;
radioButtonCurrentData = false;
if (radioButtonFileData){
currentState = State::FILEDATAWANTED;}
else currentState = State::NODATA;
}
ImGui::SeparatorText(" ");

info_popup(appLanguage[Key::WvForms_warning], appLanguage[Key::No_wave_made]);
}

void AnalyzeStateManager::selectData(const std::map<Omniscope::Id, std::vector<std::pair<double, double>>> &captureData) {
// Select Device from which to get the data, else select a file from which data can be loaded
if(currentState == State::CURRENTDATAWANTED || currentState == State::CURRENTDATASELECTED){
if(ImGui::BeginCombo("##ComboDevice", "Devices & Waveforms Menu")){
this->selectCurrentDevice(captureData);
ImGui::EndCombo();
}
}
else if((currentState == State::FILEDATAWANTED) || (currentState == State::FILEDATASELECTED)){
ImGui::InputTextWithHint("##inputLabel", appLanguage[Key::Csv_file],
&fileNameBuf, ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if(ImGui::Button(appLanguage[Key::Browse])) {
AnalyzeFileBrowser.Open();
}
// Display FileBrowser each Frame, closes automatically when a file is selected
AnalyzeFileBrowser.SetTitle("Searching for .csv files");
AnalyzeFileBrowser.Display();

if(AnalyzeFileBrowser.HasSelected()){
if(fs::path(AnalyzeFileBrowser.GetSelected().string()).extension() == ".csv"){
fileNameBuf = AnalyzeFileBrowser.GetSelected().string();
AnalyzeFileBrowser.ClearSelected();
currentState = State::FILEDATASELECTED;
}
else {
ImGui::OpenPopup(appLanguage[Key::Wrong_file_warning],
ImGuiPopupFlags_NoOpenOverExistingPopup);
AnalyzeFileBrowser.ClearSelected();
currentState = State::RESET;
}
}
}
// possible error popup
info_popup(appLanguage[Key::Wrong_file_warning],
appLanguage[Key::Wrong_file_type]);
}

void AnalyzeStateManager::loadAndSendData( const std::map<Omniscope::Id, std::vector<std::pair<double, double>>> &captureData){
// disableAllOtherFields();

std::vector<double> loadedData = this->loadData(captureData); // loading the data

if(currentState != State::DATAISSENDING){ // start asynch task to load data
loadedDataJSON["meta"] = {"metadaten"};
loadedDataJSON["data"] = {{"sampling_rate", 100000}, {"y_values", loadedData}};

future = std::async(std::launch::async, [&] {
// take temp object returned from dump() and send it to sendData
std::string result = sendData(loadedDataJSON.dump());
return result;
});
currentState = State::DATAISSENDING;
}
//sendData() from sendData.hpp on the Json.dump() output needs enableAllOtherFields(); after api_message is received is finished
}

std::vector<double> AnalyzeStateManager::loadData(const std::map<Omniscope::Id, std::vector<std::pair<double, double>>> &captureData){
std::vector<double> y_values;
if(currentState == State::CURRENTDATASELECTED){
auto selectedDevice(captureData.find(selectedDeviceId));
if(selectedDevice != captureData.end()){
y_values.resize(selectedDevice->second.size());
for(int i = 0; i < selectedDevice->second.size(); ++i){
y_values[i] = selectedDevice->second[i].second;
}
}
currentState = State::CURRENTDATALOADED;
}
else if (currentState == State::FILEDATASELECTED){
fs::path filePath = fs::path(fileNameBuf);
std::ifstream selectedFile(filePath, std::ios::binary);

if(!selectedFile.is_open()){
fmt::println("Failed to open file {}", filePath);
}
else {
// pop first line
std::string first_line;
std::getline(selectedFile, first_line);
// read the values from the file
double value{};
while (selectedFile >> value) {
y_values.emplace_back(value);
static constexpr size_t bigNumber{10'000'000};
selectedFile.ignore(bigNumber, '\n');
}

if (!y_values.empty()) {
y_values.pop_back();
}
y_values.pop_back(); // pop last element
currentState = State::FILEDATALOADED;
}
}

return y_values;
};

void AnalyzeStateManager::reset() {
radioButtonCurrentData = false;
radioButtonFileData = false;
selectedDeviceId = {};
currentState = State::NODATA;
analyzeSaved = false;
}

void AnalyzeStateManager::setMetaData() {
// will be implemented when there are analysis where Metadata is needed
}
void AnalyzeStateManager::clearMetaData() {
// will be implemented when there are analysis where Metadata is needed
}

// extra functions:

void AnalyzeStateManager::selectCurrentDevice(const std::map<Omniscope::Id, std::vector<std::pair<double, double>>> &captureData) {
for(const auto &[deviceId, values] : captureData){
if(ImGui::Checkbox(deviceId.serial.c_str(), &DeviceChecked)){
if(DeviceChecked){
selectedDeviceId = deviceId;
currentState = State::CURRENTDATASELECTED;
}
else {
selectedDeviceId = {};
currentState = State::CURRENTDATAWANTED;
}
}
}
}

void AnalyzeStateManager::whileSendingProcess(){
auto status = future.wait_for(std::chrono::milliseconds(10));
if(status == std::future_status::ready){
if(future.valid()){
apiResponse = future.get();
}
loadedDataJSON.clear();
currentState = State::DATAWASSEND;
}
else {
ImGui::SameLine();
ImGui::Text(" sending ... %c",
"|/-\\"[(int)(ImGui::GetTime() / 0.05f) & 3]);
}
}

void AnalyzeStateManager::generateAnalyzeAnswerPopUp(bool &LOADANALYSISDATA){
if(ImGui::BeginPopupModal(appLanguage[Key::Data_upload]), nullptr,
ImGuiWindowFlags_AlwaysAutoResize){
ImGui::Text(appLanguage[Key::Analyse_Answer_Text]);
ImGui::SeparatorText(" ");
ImGui::Text(apiResponse == "empty" ? appLanguage[Key::Upload_failure]
: appLanguage[Key::Upload_success]);
ImGui::SeparatorText(" ");
if(!analyzeSaved){
this->writeAnalysisAnswerIntoFile();
}
if(ImGui::Button(appLanguage[Key::SeeAnalyzeResults])){
reset();
LOADANALYSISDATA = true;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if(ImGui::Button(appLanguage[Key::Back])) {
reset();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}

void AnalyzeStateManager::writeAnalysisAnswerIntoFile() {
if (apiResponse != "empty") {
fs::path complete_path = fs::current_path() / "analyze";
if (!fs::exists(complete_path))
fs::create_directories(complete_path);

outputFilePath = complete_path / ("Analysis_" + fs::path(fileNameBuf).filename().string());

std::ofstream writefile(outputFilePath, std::ios::trunc);
if (!writefile.is_open()) {
writefile.clear();
fmt::println("Could not create {} for writing!", outputFilePath.string());
} else {
fmt::println("Start saving {}.", outputFilePath.string());
bool test = saveAsCSV(apiResponse);
/*writefile << apiResponse;
writefile.flush();
writefile.close();*/
fmt::println("Finished saving CSV file.");
}
analyzeSaved = true;
}
}

bool AnalyzeStateManager::saveAsCSV(const std::string& apiResponse) {
try {
// Parse den API-Response-String in ein JSON-Objekt
nlohmann::json jsonData = nlohmann::json::parse(apiResponse);

// Öffne die Ausgabedatei im Schreibmodus
std::ofstream outFile(outputFilePath);
if (!outFile.is_open()) {
std::cerr << "Fehler beim Öffnen der Datei zum Schreiben." << std::endl;
return false;
}

// Schreibe die Metadatenzeile
outFile << "FFT Analyse\n"; // Erste Zeile mit dem Inhalt "FFT"

// Schreibe die Einheitenzeile
outFile << "Hz,V\n"; // Zweite Zeile mit den Einheiten "frq" und "amp"

// Schleife über die "fft"-Daten im JSON-Array und schreibe sie in die Datei
for (const auto& item : jsonData["fft"]) {
double frequency = item["frequency"];
double amplitude = item["amplitude"];

// Schreibe die Frequenz und Amplitude, getrennt durch ein Komma
outFile << frequency << "," << amplitude << "\n";
}

// Schließe die Datei
outFile.close();
return true;
}
catch (const std::exception& e) {
std::cerr << "Fehler beim Verarbeiten der JSON-Daten: " << e.what() << std::endl;
return false;
}
}

fs::path AnalyzeStateManager::GetFilePath(){
return this->outputFilePath;
}
Loading
Loading