diff --git a/CMakeLists.txt b/CMakeLists.txt index f671261b..e135b479 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_PROJECT_TOP_LEVEL_INCLUDE set(CMAKE_TOOLCHAIN_FILE "${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake") -project("OmniscopeGui" VERSION 0.9.0) +project("OmniscopeGui" VERSION 1.0.2) if(MSVC) add_compile_options( @@ -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) diff --git a/src/analyze_data.cpp b/src/analyze_data.cpp new file mode 100644 index 00000000..a69ddc11 --- /dev/null +++ b/src/analyze_data.cpp @@ -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>> + &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>> + &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>> &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>> &captureData){ + // disableAllOtherFields(); + + std::vector 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 AnalyzeStateManager::loadData(const std::map>> &captureData){ + std::vector 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>> &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; +} \ No newline at end of file diff --git a/src/analyze_data.hpp b/src/analyze_data.hpp new file mode 100644 index 00000000..9a617a2d --- /dev/null +++ b/src/analyze_data.hpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "languages.hpp" +#include "popups.hpp" +#include "sendData.hpp" +#include "../imgui-stdlib/imgui_stdlib.h" +#include "jasonhandler.hpp" +#include "handler.hpp" +// declare functions and classes + +fs::path generate_analyze_menu( + bool &,bool &, + const std::map>> &); + +// Menustates +enum class State { + NODATA, + FILEDATAWANTED, + FILEDATASELECTED, + FILEDATALOADED, + CURRENTDATAWANTED, + CURRENTDATASELECTED, + CURRENTDATALOADED, + DATAISSENDING, + DATAWASSEND, + RESET +}; + +class AnalyzeStateManager { +public: + AnalyzeStateManager() { + currentState = State::NODATA; + radioButtonCurrentData = false; + radioButtonFileData = false; + DeviceChecked = false; + analyzeSaved = false; + selectedDeviceId = {}; + } + ~AnalyzeStateManager() = default; + + void setState(State state = State::NODATA); + State getState(); + + void selectDataType( + bool &, + const std::map>> &); + void selectData(const std::map>> &); + std::vector loadData(const std::map>> &); + + void loadAndSendData(const std::map>> &); + + void generateAnalyzeAnswerPopUp(bool &); + + void reset(); + + void setMetaData(); + void clearMetaData(); + + // extra functions: + + void selectCurrentDevice(const std::map>> &); + void whileSendingProcess(); + + void writeAnalysisAnswerIntoFile(); + + bool saveAsCSV(const std::string&); + + fs::path GetFilePath(); + + +private: + State currentState; + const std::map>> data; + + // extra variables for functions + bool radioButtonCurrentData, radioButtonFileData, DeviceChecked, analyzeSaved; + Omniscope::Id selectedDeviceId; + ImGui::FileBrowser AnalyzeFileBrowser; + std::string fileNameBuf; // path for a chosen file to load data from + std::future future; + nlohmann::ordered_json loadedDataJSON; + std::string apiResponse; + fs::path outputFilePath; +}; \ No newline at end of file diff --git a/src/generateTrainingData.cpp b/src/generateTrainingData.cpp index 14398a46..85e83fa5 100644 --- a/src/generateTrainingData.cpp +++ b/src/generateTrainingData.cpp @@ -103,6 +103,8 @@ void generateTrainingData( static auto vinGrayFlag = ImGuiInputTextFlags_None; static auto milGrayFlag = ImGuiInputTextFlags_None; + //set Input Fields: Sets Input Fields as metadata from file if file exists + auto setInptFields = [&](const fs::path &filename) { constexpr size_t fieldsSize{3}; // Measurement, Vin and Mileage std::vector FieldsData(fieldsSize); @@ -154,6 +156,7 @@ void generateTrainingData( static std::string fileNameBuf; static std::string analyzedWavefile; + // Checks if a file has the .csv format auto isWrongFile = [&](const fs::path &path) { if (path.extension() == ".csv") { fileNameBuf = path.string(); diff --git a/src/handler.cpp b/src/handler.cpp index 14e6e8fe..6793bcf1 100644 --- a/src/handler.cpp +++ b/src/handler.cpp @@ -23,7 +23,7 @@ static std::vector plotAxes; static std::vector getDeviceInfos() { std::vector axisInfos; std::vector samplerDvcs; // store live devices - std::vector> assignedEgus; + std::vector> assignedEgus; if (sampler.has_value()) for (auto const &device : sampler->sampleDevices) { std::string egu = @@ -32,27 +32,38 @@ static std::vector getDeviceInfos() { auto deviceId = id.value(); samplerDvcs.push_back(deviceId); if (captureData.contains(deviceId)) { - if (!std::ranges::contains(assignedEgus, egu, - &std::pair::first)) - if (assignedEgus.size() <= 3) { - // make sure ImAxis_ values haven't changed - static_assert((ImAxis_Y1 + 1) == ImAxis_Y2); - ImAxis_ nextYAxis = - static_cast(ImAxis_Y1 + assignedEgus.size()); - assignedEgus.push_back(std::make_pair(egu, nextYAxis)); - } else { - fmt::print("too many Axes added, egu not added: " - "{}\nDevice id: {}", - egu, id.value()); - break; - } - axisInfos.push_back({{deviceId, std::ref(captureData[deviceId])}, - assignedEgus.back(), - std::to_string(deviceId.sampleRate)}); - } + const int maxAxes = 3; // max count of axis + + auto deviceIdExists = std::find_if(assignedEgus.begin(), assignedEgus.end(), + [&deviceId](const auto& pair) { + return pair.first == deviceId; + }) != assignedEgus.end(); + + if (!deviceIdExists && assignedEgus.size() < maxAxes) { + static_assert((ImAxis_Y1 + 1) == ImAxis_Y2, "Achsenwerte haben sich geändert"); + + ImAxis_ nextYAxis = static_cast(ImAxis_Y1 + assignedEgus.size()); + + // New Axis if less than 3 axis are used + if (assignedEgus.size() < maxAxes) { + assignedEgus.emplace_back(std::make_pair(deviceId, nextYAxis)); + } else { + fmt::print("Zu viele Achsen hinzugefügt. Keine weitere EGU-Achse für: {}\nDevice id: {}", egu, deviceId.serial); + } + } else { + // Error if to many axis would be used + fmt::print("Maximale Anzahl an Achsen (3) erreicht oder Achse bereits vorhanden. " + "Keine weitere Achse hinzugefügt für: {}\nDevice id: {}", egu, deviceId.serial); + } + + // Speichern der Achse in axisInfos + axisInfos.push_back({{deviceId, std::ref(captureData[deviceId])}, + {egu, assignedEgus.back().second}, + std::to_string(deviceId.sampleRate)}); } else fmt::println("Error no device id found"); } + } // also get loaded files info for (auto &[device, values] : captureData) if (std::ranges::find(samplerDvcs, device.serial, &Omniscope::Id::serial) == @@ -63,7 +74,7 @@ static std::vector getDeviceInfos() { return axisInfos; } -void addPlots(const char *name, +void addPlots(const char *name, fs::path &AnalyzedFilePath, bool &LOADANALYSISDATA, std::function axesSetup) { static std::set firstRun; @@ -71,7 +82,18 @@ void addPlots(const char *name, // TODO search devices must work aswell plotAxes = getDeviceInfos(); - if (ImPlot::BeginPlot(name, plotRegion, ImPlotFlags_NoFrame)) { + if (ImPlot::BeginPlot(name, plotRegion)) { + ImPlot::SetupAxis(ImAxis_X1, "[x]"); + ImPlot::SetupAxis(ImAxis_Y1, "[y]"); + + if(!AnalyzedFilePath.empty() && LOADANALYSISDATA){ + AddPlotFromFile(AnalyzedFilePath); + ImPlot::EndPlot(); + ImPlot::PopStyleColor(); + } + else { + + // TODO: if bool areFilesLoading = false this , else AddPlotFromFile double x_min = std::numeric_limits::max(); double x_max = std::numeric_limits::min(); @@ -129,8 +151,8 @@ void addPlots(const char *name, colorMap[plot.data.first][2], 1.0f}); addPlot(plot.data, plot.egu.second); } - ImPlot::EndPlot(); + } } } @@ -357,3 +379,146 @@ void rstSettings(const decltype(captureData) &loadedFiles) { ++it; } } + +//TODO : Set this also up for saved OmniScope files + +void AddPlotFromFile(fs::path &filePath) { + LoadedFiles loadedFile; + loadedFile.LoadFromFile(filePath); + + if (loadedFile.units.size() >= 2) { + ImPlot::SetupAxis(ImAxis_X1, loadedFile.units[0].c_str()); + ImPlot::SetupAxis(ImAxis_Y1, loadedFile.units[1].c_str()); + } else { + // If the user used a wrong file or the analysis went wrong + std::cerr << "Error: Not enough units provided for axis labels." << std::endl; + return; + } + + std::map aggregated_data; + + + std::vector x_values; + std::vector y_values; + + std::vector filtered_x_values; + std::vector filtered_y_values; + + for (const auto& pair : loadedFile.data) { + x_values.push_back(pair.first); + y_values.push_back(pair.second); + } + + for (size_t i = 0; i < x_values.size(); ++i) { + if (x_values[i] >= 1 && x_values[i] <= 12500) { // only fre between 1 and 12500 hz as well as rounded freq + double rounded_x = std::round(x_values[i]); + + if (aggregated_data.find(rounded_x) != aggregated_data.end()) { + aggregated_data[rounded_x] += y_values[i]; + } else { + aggregated_data[rounded_x] = y_values[i]; + } + } + } + + for (const auto& pair : aggregated_data) { + filtered_x_values.push_back(pair.first); + filtered_y_values.push_back(pair.second); + } + + if (!filtered_x_values.empty() && !filtered_y_values.empty()) { + ImPlot::PushStyleColor(ImPlotCol_Fill, ImVec4{0.686f, 0.0f, 0.007f, 1.000f}); + ImPlot::SetNextLineStyle(ImVec4{0.686f, 0.0f, 0.007f, 1.000f}); + + ImPlot::PlotBars(filePath.string().c_str(), + filtered_x_values.data(), + filtered_y_values.data(), + static_cast(filtered_x_values.size()), + 0.001,0,0, + sizeof(double)); + + } +} + + +void LoadedFiles::LoadFromFile(fs::path &filePath) { + std::ifstream file(filePath, std::ios::binary); + if (!file.is_open()) { + std::cerr << "Fehler: Datei konnte nicht geöffnet werden." << std::endl; + } + else { + + std::string line; + int lineNumber = 0; + + while (std::getline(file, line)) { + lineNumber++; + + // Erste Zeile: Metadaten + if (lineNumber == 1) { + + } + // Zweite Zeile: Einheiten + else if (lineNumber == 2) { + parseUnits(line); + } + // Ab der dritten Zeile: Datenpaare + else { + parseData(line); + } + } + + file.close(); + } +} + +void LoadedFiles::printData(){ + + std::cout << "Einheiten: "; + for (const auto& unit : units) { + std::cout << unit << " "; + } + std::cout << std::endl; + + std::cout << "Daten: " << std::endl; + for (const auto& pair : data) { + std::cout << pair.first << ", " << pair.second << std::endl; + } +} + +// Private Methoden zur Verarbeitung der CSV-Daten +void LoadedFiles::parseUnits(const std::string& line) { + std::stringstream ss(line); + std::string unit; + + // Einheiten durch Kommas getrennt + while (std::getline(ss, unit, ',')) { + units.push_back(trim(unit)); // to not load space or \n + } +} + +std::string trim(const std::string& str) { + std::string trimmed = str; + trimmed.erase(std::remove_if(trimmed.begin(), trimmed.end(), ::isspace), trimmed.end()); + return trimmed; +} + +void LoadedFiles::parseData(const std::string& line) { + std::stringstream ss(line); + std::string value1_str, value2_str; + + // Lese die beiden Werte als Strings, getrennt durch Komma oder Leerzeichen + std::getline(ss, value1_str, ','); // Lese den ersten Wert bis zum Komma + std::getline(ss, value2_str); // Lese den zweiten Wert (der Rest der Zeile) + + // Entferne eventuelle zusätzliche Leerzeichen + value1_str.erase(remove_if(value1_str.begin(), value1_str.end(), isspace), value1_str.end()); + value2_str.erase(remove_if(value2_str.begin(), value2_str.end(), isspace), value2_str.end()); + + // Konvertiere die Strings in double-Werte + double value1 = std::stod(value1_str); + double value2 = std::stod(value2_str); + + // Speichere das Paar in der Datenstruktur + data.emplace_back(value1, value2); +} \ No newline at end of file diff --git a/src/handler.hpp b/src/handler.hpp index f5daa0c9..751c0a97 100644 --- a/src/handler.hpp +++ b/src/handler.hpp @@ -19,7 +19,7 @@ inline std::map>> captureData; void addPlots( - const char *, + const char *,fs::path &,bool &, std::function); void parseDeviceMetaData(Omniscope::MetaData, std::shared_ptr &); @@ -31,5 +31,27 @@ void set_config(const std::string &); void set_json(nlohmann::json &); void set_inital_config(nlohmann::json &); void rstSettings(const decltype(captureData) &); +void AddPlotFromFile(fs::path &); + +std::string trim(const std::string &); + +// Struct for loading data from a file : + +struct LoadedFiles{ + public: + void LoadFromFile(fs::path &); + void printData(); // for debugging + void addToPlot(); + bool isScopeData; + std::vector units; + std::vector> data; + + private: + void parseData(const std::string &); + void parseUnits(const std::string &); + +}; + + #endif diff --git a/src/languages.hpp b/src/languages.hpp index 3506ab91..7788e47d 100644 --- a/src/languages.hpp +++ b/src/languages.hpp @@ -90,7 +90,11 @@ enum class Key { Load_file_data, Load_file, Load_another_file, - Path + Path, + FFT_Analyze, + Analyse_Answer_Text, + SeeAnalyzeResults, + MathematicalAnalysis }; inline const std::map englishLan{ @@ -125,7 +129,7 @@ inline const std::map englishLan{ {Key::Save, "Save"}, {Key::Save_warning, "Save warning! "}, {Key::No_dvc_available, "No devices are available ..."}, - {Key::AnalyzeData, "Analyze\nData"}, + {Key::AnalyzeData, "Analyze data"}, {Key::Crt_trng_data, "Create\nTraining\nData"}, {Key::Back, "Back"}, {Key::Start, "Start"}, @@ -179,7 +183,11 @@ inline const std::map englishLan{ {Key::Load_file_data, "Load file data"}, {Key::Load_another_file, "Load another file"}, {Key::Path, "Path"}, - {Key::ADC_counts, "ADC counts"}}; + {Key::ADC_counts, "ADC counts"}, + {Key::FFT_Analyze, "FFT Analysis"}, + {Key::Analyse_Answer_Text, "Your analysis results are ready! \n They are saved in the /analyze folder."}, + {Key::SeeAnalyzeResults, "Load results"}, + {Key::MathematicalAnalysis, "Mathematical Analysis"}}; inline const std::map germanLan{ {Key::Known_Car, "Fahrzeugauswahl"}, @@ -200,7 +208,7 @@ inline const std::map germanLan{ {Key::Measure_not_saved, "Die Messung wurde nicht gespeichert!\n" "Möchten Sie es speichern, bevor Sie es löschen?\n"}, - {Key::Version, "Ausführung"}, + {Key::Version, "Version"}, {Key::Diagnostics, "Diagnose"}, {Key::Compression, "Kompression"}, {Key::Anlyz_crnt_waveform, "Aktuelle Wellenform analysieren"}, @@ -214,7 +222,7 @@ inline const std::map germanLan{ {Key::Save, "Speichern"}, {Key::Save_warning, "Speicherwarnung!"}, {Key::No_dvc_available, "Es sind keine Geräte verfügbar ..."}, - {Key::AnalyzeData, "Daten\nAnalysieren"}, + {Key::AnalyzeData, "Daten analysieren"}, {Key::Crt_trng_data, "Erstellen\nSie\nTrainingsdaten"}, {Key::Back, "Zurück"}, {Key::Start, "Start"}, @@ -268,7 +276,11 @@ inline const std::map germanLan{ {Key::Load_file_data, "Alte Daten laden"}, {Key::Load_another_file, "Eine weitere Datei laden"}, {Key::Path, "Pfad"}, - {Key::ADC_counts, "ADC Zählungen"}}; + {Key::ADC_counts, "ADC Zählungen"}, + {Key::FFT_Analyze, "FFT Analyse"}, + {Key::Analyse_Answer_Text, "Deine Analyseergebnisse sind da ! \n Sie befinden sich im /analyze Ordner"}, + {Key::SeeAnalyzeResults, "Lade Ergebnisse"}, + {Key::MathematicalAnalysis, "Mathematische Analysen"}}; inline auto appLanguage = englishLan; namespace fs = std::filesystem; diff --git a/src/main.cpp b/src/main.cpp index e3d1f8dc..39a9714a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include "settingspopup.hpp" #include "style.hpp" #include +#include "analyze_data.hpp" int main() { const std::string configpath = "config/config.json"; @@ -12,12 +13,16 @@ int main() { load_json_file(load_json(config, "languagepath") + load_json(config, "language") + ".json"); // local variables - bool flagPaused{true}, development{false}, open_generate_training_data{false}, + bool flagPaused{true}, development{false}, open_generate_training_data{false},open_analyze_menu{false}, open_settings{false}; std::once_flag configFlag; auto loadedFiles = captureData; std::map loadedFilenames; + // temporary solution + bool LOADANALYSISDATA{false}; + fs::path AnalyzedFilePath(""); + // main loop auto render = [&]() { std::call_once(configFlag, set_inital_config, std::ref(config)); @@ -40,8 +45,8 @@ int main() { ImGui::BeginChild("Left Side", {windowSize.x * .18f, 0.f}); // ############################################# Side Menu - set_side_menu(config, open_settings, open_generate_training_data, - loadedFiles, loadedFilenames); + set_side_menu(config, open_settings, open_generate_training_data,open_analyze_menu, + loadedFiles, loadedFilenames, LOADANALYSISDATA); // there're four "BeginChild"s, one as the left side // and three on the right side ImGui::EndChild(); // end child "Left Side" @@ -51,7 +56,7 @@ int main() { sampler->copyOut(captureData); // ######################################### Toolbar - set_toolbar(config, language, flagPaused, loadedFiles); + set_toolbar(config, language, flagPaused, loadedFiles, LOADANALYSISDATA); // ############################ Settings Menu static int title = 0; @@ -79,26 +84,29 @@ int main() { generateTrainingData(open_generate_training_data, captureData, savedFileNames); + // Generate analyze data popup + if (open_analyze_menu) + AnalyzedFilePath = generate_analyze_menu(open_analyze_menu, LOADANALYSISDATA, captureData); + // ############################ addPlots("Recording the data", ...) ImGui::Dummy({0.f, windowSize.y * .01f}); PushPlotRegionColors(); - ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, windowSize.x * .009f); - ImGui::BeginChild("Record Data", {0.f, windowSize.y * 0.62f}, + ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, windowSize.x * .005f); + ImGui::BeginChild("Record Data", {0.f, windowSize.y * 0.64f}, ImGuiChildFlags_Border); // Axes 1 to 3 // Check if time base for axes are same // check if egu and timescale for plot are same // error if third device is added addPlots( - appLanguage[Key::Recording_Data], + appLanguage[Key::Recording_Data],AnalyzedFilePath,LOADANALYSISDATA, [flagPaused](double x_max, std::string yLabel, ImAxis_ axis, double yMin, double yMax) { - ImPlot::SetupLegend(ImPlotLocation_NorthEast); + ImPlot::SetupLegend(ImPlotLocation_NorthWest); // auto auxFlagsMeasuring = // ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_NoGridLines; // auto auxFlagsPaused = ImPlotAxisFlags_NoGridLines; - ImPlot::SetupAxisTicks(ImAxis_Y1, -10, 200, 22, nullptr, true); - + //ImPlot::SetupAxisTicks(ImAxis_Y1, -10, 200, 22, nullptr, true); if (!flagPaused) { ImPlot::SetupAxis(axis, yLabel.c_str(), ImPlotAxisFlags_AutoFit); ImPlot::SetupAxis(ImAxis_X1, appLanguage[Key::Time_sec], @@ -152,6 +160,7 @@ int main() { loadedFilenames.erase(it->first); loadedFilesChkBxs[it->first].b = false; it = loadedFiles.erase(it); + AnalyzedFilePath = ""; } else it++; ImGui::PopID(); diff --git a/src/style.cpp b/src/style.cpp index 475dcada..6c5895ee 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -12,6 +12,7 @@ #include "languages.hpp" #include "../imgui-filebrowser/imfilebrowser.h" #include "popups.hpp" +#include "analyze_data.hpp" void SetupImGuiStyle(bool bStyleDark_, float alpha_) { @@ -248,9 +249,9 @@ bool LoadTextureFromHeader(unsigned char const *png_data, int png_data_len, } void set_side_menu(const nlohmann::json &config, bool &open_settings, - bool &open_generate_training_data, + bool &open_generate_training_data, bool &open_analyze_menu, decltype(captureData) &loadedFiles, - std::map &loadedFilenames) { + std::map &loadedFilenames, bool &LOADANALYSISDATA) { auto windowSize{ImGui::GetIO().DisplaySize}; // Initializing all variables for images @@ -294,6 +295,7 @@ void set_side_menu(const nlohmann::json &config, bool &open_settings, ImGui::ImageButtonWithText( (void *)(intptr_t)image_texture[PngRenderedCnt], appLanguage[Key::Dvc_search])) { + LOADANALYSISDATA= false; devices.clear(); deviceManager.clearDevices(); initDevices(); @@ -313,21 +315,28 @@ void set_side_menu(const nlohmann::json &config, bool &open_settings, static bool showDiag = false; const bool showDiagPrev = showDiag; - ImGui::PushStyleColor(ImGuiCol_Text, {0.8f, 0.8f, 0.8f, 0.7f}); + /*ImGui::PushStyleColor(ImGuiCol_Text, {0.8f, 0.8f, 0.8f, 0.7f}); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, {0.8f, 0.8f, 0.8f, 0.0f}); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, {0.8f, 0.8f, 0.8f, 0.0f}); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, {0.8f, 0.8f, 0.8f, 0.0f});*/ if (loaded_png[++PngRenderedCnt] && // render Diagnostics ImGui::ImageButtonWithText( (void *)(intptr_t)image_texture[PngRenderedCnt], appLanguage[Key::Diagnostics])) { - //showDiag = !showDiag; + showDiag = !showDiag; } - ImGui::PopStyleColor(3); + //ImGui::PopStyleColor(3); - /*if (showDiag && !showDiagPrev) + if (showDiag && !showDiagPrev) ImGui::SetNextItemOpen(false); - if (showDiag && ImGui::TreeNode(appLanguage[Key::Battery_measure])) { + if (showDiag && ImGui::TreeNode(appLanguage[Key::MathematicalAnalysis])) { + if (ImGui::Button(appLanguage[Key::FFT_Analyze])){ + open_analyze_menu = true; + showDiag = false; + } + ImGui::TreePop(); + } + /*if (showDiag && ImGui::TreeNode(appLanguage[Key::Battery_measure])) { ImGui::PushStyleColor(ImGuiCol_Text, inctColStyle); if (ImGui::Button(appLanguage[Key::Anlyz_crnt_waveform])) showDiag = false; @@ -357,12 +366,12 @@ void set_side_menu(const nlohmann::json &config, bool &open_settings, } ImGui::SetCursorPosY(windowSize.y * 0.9f); ImGui::Text(fmt::format("{}: {}", appLanguage[Key::Version], - "1.0.1") + "1.0.2") .c_str()); } void set_toolbar(const nlohmann::json &config, const nlohmann::json &language, - bool &flagPaused, const decltype(captureData) &loadedFiles) { + bool &flagPaused, const decltype(captureData) &loadedFiles,bool &LOADANALYSISDATA) { // variable declaration static auto now = std::chrono::system_clock::now(); @@ -490,6 +499,7 @@ void set_toolbar(const nlohmann::json &config, const nlohmann::json &language, if (flagDataNotSaved) { ImGui::OpenPopup(appLanguage[Key::Reset_q]); } else { + LOADANALYSISDATA = false; rstSettings(loadedFiles); flagPaused = true; } diff --git a/src/style.hpp b/src/style.hpp index 31de25b3..1c9ed56b 100644 --- a/src/style.hpp +++ b/src/style.hpp @@ -17,11 +17,11 @@ inline constexpr ImVec4 normColStyle{0.1f, 0.1f, 0.1f, 1.f}; void SetupImGuiStyle(bool, float); void set_button_style_to(const nlohmann::json &, const std::string &); bool LoadTextureFromHeader(unsigned char const *, int, GLuint *, int *, int *); -void set_side_menu(const nlohmann::json &, bool &, bool &, +void set_side_menu(const nlohmann::json &, bool &, bool &, bool &, decltype(captureData) &, - std::map &); + std::map &, bool &); void set_toolbar(const nlohmann::json &, const nlohmann::json &, bool &, - const decltype(captureData) &); + const decltype(captureData) &, bool&); void PopupStyleEditor(); void PushPlotRegionColors(); void PopPlotRegionColors();