diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..9a2bb54b3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: unconfirmed bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - OpenFLUID version [e.g. 2.1.11] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/workflows/CI-macos.yaml b/.github/workflows/CI-macos.yaml index ce51980a2..d9d04210e 100644 --- a/.github/workflows/CI-macos.yaml +++ b/.github/workflows/CI-macos.yaml @@ -23,12 +23,6 @@ jobs: gnuplot - name: Installation of pandoc uses: r-lib/actions/setup-pandoc@v2 - - name: Installation of latex - uses: teatimeguest/setup-texlive-action@v3 - with: - packages: | - scheme-full - bibtex - name: Checkout of source code uses: actions/checkout@v3 - name: Preparation @@ -36,7 +30,7 @@ jobs: echo "$(brew --prefix qt6)/bin" >> $GITHUB_PATH mkdir ./_build - name: Configuration - run: cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix qt6)/lib/cmake + run: cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix qt6)/lib/cmake -DOFBUILD_ENABLE_DOCS=OFF working-directory: ./_build - name: Build run: make -j 2 diff --git a/.github/workflows/CI-windows.yaml b/.github/workflows/CI-windows.yaml index 2e71e7358..40c1eed67 100644 --- a/.github/workflows/CI-windows.yaml +++ b/.github/workflows/CI-windows.yaml @@ -10,7 +10,7 @@ jobs: ci-windows-debug: - runs-on: windows-2019 + runs-on: windows-2025 defaults: run: @@ -25,7 +25,7 @@ jobs: release: false install: >- mingw-w64-x86_64-toolchain mingw-w64-x86_64-gcc mingw-w64-x86_64-gcc-fortran - mingw-w64-x86_64-cmake mingw-w64-x86_64-doxygen + mingw-w64-x86_64-cmake mingw-w64-x86_64-doxygen make mingw-w64-x86_64-boost mingw-w64-x86_64-gdal mingw-w64-x86_64-curl-winssl mingw-w64-x86_64-qt5 mingw-w64-x86_64-openssl git p7zip mingw-w64-x86_64-gnuplot mingw-w64-x86_64-graphviz diff --git a/CMake.in.cmake b/CMake.in.cmake index 90939b6da..0123ee43a 100644 --- a/CMake.in.cmake +++ b/CMake.in.cmake @@ -1,7 +1,8 @@ # # Configuration file for CMakeLists.txt files # -# Author : Jean-Christophe FABRE +# Authors : Jean-Christophe FABRE +# Armel THÖNI # # This file is included by the main CMakeLists.txt file, and defines variables # to configure the build and install @@ -22,8 +23,8 @@ SET(OFBUILD_CUSTOM_CMAKE_VERSION "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}. SET(OPENFLUID_VERSION_MAJOR 2) SET(OPENFLUID_VERSION_MINOR 2) -SET(OPENFLUID_VERSION_PATCH 0) -SET(OPENFLUID_VERSION_STATUS "") # example: SET(OPENFLUID_VERSION_STATUS "rc1") +SET(OPENFLUID_VERSION_PATCH 1) +SET(OPENFLUID_VERSION_STATUS "alpha6") # example: SET(OPENFLUID_VERSION_STATUS "rc1") SET(OPENFLUID_VERSION_FULL "${OPENFLUID_VERSION_MAJOR}.${OPENFLUID_VERSION_MINOR}.${OPENFLUID_VERSION_PATCH}") diff --git a/cmake/OpenFLUIDTestScript.cmake b/cmake/OpenFLUIDTestScript.cmake index c888daa01..ae58fb488 100644 --- a/cmake/OpenFLUIDTestScript.cmake +++ b/cmake/OpenFLUIDTestScript.cmake @@ -54,6 +54,13 @@ FUNCTION(EXECUTE_COMMAND CMD) ENDIF() FILE(REMOVE_RECURSE ${TMPDIR}) + ELSEIF(${CMD} STREQUAL "CHECK_FILE_CONTAINS") + FILE(READ ${TMPDIR}/${ARGV1} TMPTXT) + STRING(FIND "${TMPTXT}" "${ARGV2}" ISMATCH) + IF(${ISMATCH} EQUAL -1) + MESSAGE(FATAL_ERROR "CHECK_FILE_CONTAINS: file ${ARGV1} does not contain string ${ARGV2}") + ENDIF() + ELSEIF(${CMD} STREQUAL "CHECK_FILE_IDENTICAL") # generates consistent eol between files (can be replaced by option ignore_eol in cmake 3.14) CONFIGURE_FILE(${ARGV1}${ARGV3} ${ARGV1}${ARGV3}_UNIX_EOL NEWLINE_STYLE UNIX) @@ -73,6 +80,8 @@ FUNCTION(EXECUTE_COMMAND CMD) IF(IS_DIRECTORY ${ARGV1}) MESSAGE(FATAL_ERROR "CHECK_DIRECTORY_NOT_EXISTING: directory ${ARGV1} found") ENDIF() + ELSEIF(${CMD} STREQUAL "CREATE_DIRECTORY") + FILE(MAKE_DIRECTORY ${ARGV1}) ELSEIF(${CMD} STREQUAL "REMOVE_FILE") IF(EXISTS ${ARGV1}) FILE(REMOVE ${ARGV1}) @@ -116,6 +125,7 @@ FUNCTION(PARSE_COMMANDS) IF(${CMD_EXPECTED}) IF(${ELEM} STREQUAL "CHECK_FILE_EXIST" OR ${ELEM} STREQUAL "CHECK_FILE_EXIST_IN_ARCHIVE" OR + ${ELEM} STREQUAL "CHECK_FILE_CONTAINS" OR ${ELEM} STREQUAL "CHECK_FILE_IDENTICAL" OR ${ELEM} STREQUAL "CHECK_DIRECTORY_EXIST" OR ${ELEM} STREQUAL "CHECK_DIRECTORY_NOT_EXISTING" OR @@ -125,7 +135,7 @@ FUNCTION(PARSE_COMMANDS) ${ELEM} STREQUAL "COPY_DIRECTORY" OR ${ELEM} STREQUAL "EMPTY_DIRECTORY" OR ${ELEM} STREQUAL "CREATE_FILE" OR - ${ELEM} STREQUAL "CREATE_DIR" OR + ${ELEM} STREQUAL "CREATE_DIRECTORY" OR ${ELEM} STREQUAL "COMPARE_FILES" OR ${ELEM} STREQUAL "COMPARE_DIRECTORIES") SET(CURRENT_CMD "${ELEM}") @@ -141,6 +151,7 @@ FUNCTION(PARSE_COMMANDS) ELSE() IF(${CURRENT_CMD} STREQUAL "COMPARE_FILES" OR ${CURRENT_CMD} STREQUAL "CHECK_FILE_EXIST_IN_ARCHIVE" OR + ${CURRENT_CMD} STREQUAL "CHECK_FILE_CONTAINS" OR ${CURRENT_CMD} STREQUAL "COMPARE_DIRECTORIES" OR ${CURRENT_CMD} STREQUAL "COPY_FILE" OR ${CURRENT_CMD} STREQUAL "COPY_DIRECTORY") @@ -208,7 +219,7 @@ MESSAGE("TEST command: ${CMD}") EXECUTE_PROCESS(COMMAND ${CMD} RESULT_VARIABLE CMD_RESULT) IF(CMD_RESULT) - MESSAGE(FATAL_ERROR "Test error") + MESSAGE(FATAL_ERROR "Test error: ${CMD_RESULT}") ENDIF() diff --git a/doc/contents/dev_signature.md b/doc/contents/dev_signature.md index 5bce13f5b..62bf6c85f 100644 --- a/doc/contents/dev_signature.md +++ b/doc/contents/dev_signature.md @@ -130,9 +130,7 @@ Simulator parameters, variables and attributes are declared in the "simulator" p ### Simulator parameters {#dev_signature_data_simparams} Simulator parameters are values provided to each simulator, -and are declared using the @if DocIsLaTeX **DECLARE_REQUIRED_PARAMETER** or **DECLARE_USED_PARAMETER** -@else DECLARE_REQUIRED_PARAMETER or DECLARE_USED_PARAMETER @endif - instructions. +and are declared in three blocks: "required", "used" or "produced". These instructions takes 3 arguments * the name of the parameter diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 8ed9bec6b..39d13507a 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -111,6 +111,15 @@ IF(PYTHON3_EXECUTABLE) "${CMAKE_SOURCE_DIR}" ) + # import ref wares from repositories + ADD_CUSTOM_TARGET(importwares + COMMENT "Copying reference wares from their remote repository" + COMMAND "${PYTHON3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/tools/ofsrc-importwares.py" + "${CMAKE_CURRENT_SOURCE_DIR}/tools/wares-to-import.txt" + "${CMAKE_SOURCE_DIR}" + ) + ENDIF() diff --git a/resources/tests/miscdata/SignatureSerializer/ref_sim.json b/resources/tests/miscdata/SignatureSerializer/ref_sim.json index 9a1c14c52..9b6029d15 100644 --- a/resources/tests/miscdata/SignatureSerializer/ref_sim.json +++ b/resources/tests/miscdata/SignatureSerializer/ref_sim.json @@ -93,6 +93,18 @@ "description": "coefficient", "siunit": "", "type": "" + }, + { + "name": "coeff 2", + "description": "coefficient with a space", + "siunit": "", + "type": "" + }, + { + "name": "coeff&3", + "description": "coefficient with weïrd char", + "siunit": "", + "type": "" } ] }, diff --git a/resources/tests/miscdata/SignatureSerializer/ref_sim_unordered.json b/resources/tests/miscdata/SignatureSerializer/ref_sim_unordered.json index 9a1c14c52..9b6029d15 100644 --- a/resources/tests/miscdata/SignatureSerializer/ref_sim_unordered.json +++ b/resources/tests/miscdata/SignatureSerializer/ref_sim_unordered.json @@ -93,6 +93,18 @@ "description": "coefficient", "siunit": "", "type": "" + }, + { + "name": "coeff 2", + "description": "coefficient with a space", + "siunit": "", + "type": "" + }, + { + "name": "coeff&3", + "description": "coefficient with weïrd char", + "siunit": "", + "type": "" } ] }, diff --git a/resources/tests/miscdata/SignatureSerializer/ref_sim_unordered_array.json b/resources/tests/miscdata/SignatureSerializer/ref_sim_unordered_array.json index 425a0f304..daee0e3c1 100644 --- a/resources/tests/miscdata/SignatureSerializer/ref_sim_unordered_array.json +++ b/resources/tests/miscdata/SignatureSerializer/ref_sim_unordered_array.json @@ -93,6 +93,18 @@ "description": "coefficient", "siunit": "", "type": "" + }, + { + "name": "coeff 2", + "description": "coefficient with a space", + "siunit": "", + "type": "" + }, + { + "name": "coeff&3", + "description": "coefficient with weïrd char", + "siunit": "", + "type": "" } ] }, diff --git a/resources/tests/miscsrc/wares-dev-201xx/sim/tests.cmdline.sim-migration/CMakeLists.txt b/resources/tests/miscsrc/wares-dev-201xx/sim/tests.cmdline.sim-migration/CMakeLists.txt index d78f4ce7e..d3cf6cc1b 100644 --- a/resources/tests/miscsrc/wares-dev-201xx/sim/tests.cmdline.sim-migration/CMakeLists.txt +++ b/resources/tests/miscsrc/wares-dev-201xx/sim/tests.cmdline.sim-migration/CMakeLists.txt @@ -5,4 +5,8 @@ INCLUDE(CMake.in.config) FIND_PACKAGE(OpenFLUIDHelpers REQUIRED) +SET(CUSTOM_VAR foo) + OPENFLUID_ADD_SIMULATOR(SIM) + +MESSAGE(STATUS "custom message") diff --git a/resources/tools/importsim.py b/resources/tools/ofsrc-importwares.py similarity index 86% rename from resources/tools/importsim.py rename to resources/tools/ofsrc-importwares.py index 9944f3dc3..d7991517b 100644 --- a/resources/tools/importsim.py +++ b/resources/tools/ofsrc-importwares.py @@ -32,13 +32,16 @@ # This scripts imports simulators in a destination folder by specifying the git url and branch. # It will then remove all git related files in simualors and potential fragments # -# Usage: importsim.py /path/to/sims_text_file /path/to/destination_folder +# Usage: importwares.py /path/to/sims_text_file /path/to/destination_folder # # - sims_text_file: Text file where we specify the simulators to import on each lines of the file. # A line must be written as follows : # GitURL#branch_name clone_folder_name # - /path/to/destination_folder: The destination folder path where all simulators are cloned # +# When in openfluid root dir: +# `python3 resources/tools/ofsrc-importwares.py resources/tools/wares-to-import.txt .` + ############################################################################ @@ -82,8 +85,10 @@ def clone_and_clean_repo(file_line, destination_path): repo_name = file_line_split[1] giturl_split = giturl_branch.split("#") + print("Processing "+repo_name.split("/")[-1]+"...") + if(len(giturl_split) != 2): - print("[ERROR] Wrong file format") + print(" [ERROR] Wrong file format") return git_url, git_branch = giturl_split @@ -92,7 +97,7 @@ def clone_and_clean_repo(file_line, destination_path): repo_path = os.path.join(destination_path, repo_name) if(delete_folder(repo_path)): - print(repo_path + " deleted") + print(" " + repo_path + " deleted") # Clone repo @@ -103,10 +108,10 @@ def clone_and_clean_repo(file_line, destination_path): text=True) if clone_process.returncode != 0: - print("[ERROR] Could not clone git repo " + git_url) + print(" [ERROR] Could not clone git repo " + git_url) return - print(repo_path + " successfullly cloned") + print(" " + repo_path + " successfully cloned") # Checkout branch @@ -117,10 +122,10 @@ def clone_and_clean_repo(file_line, destination_path): text=True) if branch_process.returncode != 0: - print("[ERROR] Could not checkout branch " + git_branch) + print(" [ERROR] Could not checkout branch " + git_branch) return - print(git_branch + " successfullly checkout") + print(" " + git_branch + " successfully checkout") # Update potential submodules subprocess.run(["git", "submodule", "update", "--init", "--recursive"], @@ -131,16 +136,16 @@ def clone_and_clean_repo(file_line, destination_path): # Delete versionning files git_folder = os.path.join(repo_path, ".git") if(delete_folder(git_folder)): - print(git_folder + " deleted") + print(" " + git_folder + " deleted") gitignore_file = os.path.join(repo_path, ".gitignore") if(delete_file(gitignore_file)): - print(gitignore_file + " deleted") + print(" " + gitignore_file + " deleted") # Check for submodules git_submodules_file = os.path.join(repo_path, ".gitmodules") if(delete_file(git_submodules_file)): - print(git_submodules_file + " deleted") + print(" " + git_submodules_file + " deleted") # Loop through fragments fragments_path = os.path.join(repo_path, "src", "fragments") @@ -148,11 +153,11 @@ def clone_and_clean_repo(file_line, destination_path): for fragment in os.listdir(fragments_path): fragment_git_file = os.path.join(fragments_path, fragment, ".git") if(delete_file(fragment_git_file)): - print(fragment_git_file + " deleted") + print(" " + fragment_git_file + " deleted") fragment_gitignore_file = os.path.join(fragments_path, fragment, ".gitignore") if(delete_file(fragment_gitignore_file)): - print(fragment_gitignore_file + " deleted") + print(" " + fragment_gitignore_file + " deleted") # =========================================================== diff --git a/resources/tools/ofsrc-stylecheck.py b/resources/tools/ofsrc-stylecheck.py index e05912d1a..410b2202d 100755 --- a/resources/tools/ofsrc-stylecheck.py +++ b/resources/tools/ofsrc-stylecheck.py @@ -382,15 +382,16 @@ def checkHeaderGuard(self, Filename, Content): ExpectedGuardName = '__OPENFLUID_'+MiddlePart+LastPart+'__' - if ExpectedGuardName: - Result = re.search( r'#ifndef '+re.escape(ExpectedGuardName)+'\s*\n#define '+re.escape(ExpectedGuardName), Content) - if not Result: - self.addProblem('HGRD',Filename,1,'missing or malformed header guard definition (expected',ExpectedGuardName+')') - - Result = re.search( r'#endif /\* '+re.escape(ExpectedGuardName)+ r' \*/\s*\n', Content) - if not Result: - self.addProblem('HGRD',Filename,1,'missing or malformed header guard closing') + ResultPragma = re.search( r'#pragma once', Content) + if not ResultPragma: + Result = re.search( r'#ifndef '+re.escape(ExpectedGuardName)+'\s*\n#define '+re.escape(ExpectedGuardName), Content) + if not Result: + self.addProblem('HGRD',Filename,1,'missing or malformed header guard definition (expected',ExpectedGuardName+')') + + Result = re.search( r'#endif /\* '+re.escape(ExpectedGuardName)+ r' \*/\s*\n', Content) + if not Result: + self.addProblem('HGRD',Filename,1,'missing or malformed header guard closing') diff --git a/resources/tools/wares-to-import.txt b/resources/tools/wares-to-import.txt new file mode 100644 index 000000000..ef525bb71 --- /dev/null +++ b/resources/tools/wares-to-import.txt @@ -0,0 +1,8 @@ +https://hub.openfluid-project.org/git-service/wares/simulators/water.surf.transfer-su.hayami#openfluid-2.2 examples/wares-dev/simulators/water.surf.transfer-su.hayami +https://hub.openfluid-project.org/git-service/wares/simulators/water.surf.transfer-rs.hayami#openfluid-2.2 examples/wares-dev/simulators/water.surf.transfer-rs.hayami +https://hub.openfluid-project.org/git-service/wares/simulators/water.atm-surf.rain-su.files#openfluid-2.2 examples/wares-dev/simulators/water.atm-surf.rain-su.files +https://hub.openfluid-project.org/git-service/wares/simulators/water.surf-uz.runoff-infiltration.mseytoux#openfluid-2.2 examples/wares-dev/simulators/water.surf-uz.runoff-infiltration.mseytoux +https://hub.openfluid-project.org/git-service/wares/observers/export.vars.files.csv#openfluid-2.2 src/observers/export.vars.files.csv +https://hub.openfluid-project.org/git-service/wares/observers/export.vars.files.csv-multicols#openfluid-2.2 src/observers/export.vars.files.csv-multicols +https://hub.openfluid-project.org/git-service/wares/observers/export.vars.files.kml-plot#openfluid-2.2 src/observers/export.vars.files.kml-plot +https://hub.openfluid-project.org/git-service/wares/observers/export.vars.files.kml-anim#openfluid-2.2 src/observers/export.vars.files.kml-anim \ No newline at end of file diff --git a/share/openfluid/waresdev/cmake.syntaxhl.ofdev.xml b/share/openfluid/waresdev/cmake.syntaxhl.ofdev.xml index 124816aaf..cb7e41796 100644 --- a/share/openfluid/waresdev/cmake.syntaxhl.ofdev.xml +++ b/share/openfluid/waresdev/cmake.syntaxhl.ofdev.xml @@ -483,7 +483,7 @@ - + diff --git a/share/openfluid/waresdev/migration/include/201xx-202xx/SimulatorSignatureMacros.hpp b/share/openfluid/waresdev/migration/include/201xx-202xx/SimulatorSignatureMacros.hpp index d009dc851..e803c2ae0 100644 --- a/share/openfluid/waresdev/migration/include/201xx-202xx/SimulatorSignatureMacros.hpp +++ b/share/openfluid/waresdev/migration/include/201xx-202xx/SimulatorSignatureMacros.hpp @@ -118,7 +118,7 @@ */ #define DECLARE_PRODUCED_VARIABLE(name,uclass,description,unit) \ Signature->SimulatorHandledData.ProducedVars\ - .push_back(openfluid::ware::SignatureSpatialDataItem((name),uclass,description,unit)); + .push_back(openfluid::ware::SignatureVariableItem((name),uclass,description,unit)); /** @deprecated Since version 2.1.0. Use #DECLARE_PRODUCED_VARIABLE instead @@ -130,7 +130,7 @@ */ #define DECLARE_UPDATED_VARIABLE(name,uclass,description,unit) \ Signature->SimulatorHandledData.UpdatedVars\ - .push_back(openfluid::ware::SignatureSpatialDataItem((name),uclass,description,unit)); + .push_back(openfluid::ware::SignatureVariableItem((name),uclass,description,unit)); /** @deprecated Since version 2.1.0. Use #DECLARE_UPDATED_VARIABLE instead diff --git a/src/apps/openfluid-devstudio/MainWindow.cpp b/src/apps/openfluid-devstudio/MainWindow.cpp index 620210e7e..9eec9b58c 100644 --- a/src/apps/openfluid-devstudio/MainWindow.cpp +++ b/src/apps/openfluid-devstudio/MainWindow.cpp @@ -277,6 +277,8 @@ QToolButton::menu-button:pressed, QToolButton::menu-button:hover { mp_WidgetsCollection, SLOT(openPath(const QString&))); connect(Explorer, SIGNAL(deleteWareAsked()), this, SLOT(onDeleteWareRequested())); + connect(Explorer, SIGNAL(duplicateWareAsked()), + this, SLOT(onDuplicateWareRequested())); connect(Explorer, SIGNAL(fileDeleted(const QString&)), mp_WidgetsCollection, SLOT(closeEditor(const QString&))); connect(Explorer, SIGNAL(folderDeleted(const QString&, const QString&, const bool)), @@ -724,6 +726,61 @@ void MainWindow::updateSaveButtonsStatus(bool FileModified, bool FileOpen, bool // ===================================================================== +void MainWindow::onDuplicateWareRequested() +{ + QWidget* CurrentWidget = ui->WaresTabWidget->currentWidget(); + QString SelectedPath = ""; + + if (CurrentWidget == ui->SimPage) + { + SelectedPath = ui->SimExplorer->getCurrentPath(); + } + else if (CurrentWidget == ui->ObsPage) + { + SelectedPath = ui->ObsExplorer->getCurrentPath(); + } + else if (CurrentWidget == ui->ExtPage) + { + SelectedPath = ui->ExtExplorer->getCurrentPath(); + } + + if (SelectedPath != "") + { + const auto WareInfo = openfluid::waresdev::WareSrcEnquirer::getWareInfoFromPath(SelectedPath.toStdString()); + QString WarePath = + QString::fromStdString( + WareInfo.AbsoluteWarePath); + if (WarePath != "") + { + // add dialog to know more information + bool OK; + QString NewWareName = QInputDialog::getText(this, "Ware name", + "Name of the new ware:", + QLineEdit::Normal, + QString::fromStdString(WareInfo.WareDirName+".duplicate"), + &OK); + // TOIMPL reuse here the filter checking if ware name OK (not existing + only accepted symbols) + if (OK && !NewWareName.isEmpty() && WareInfo.WareDirName != NewWareName.toStdString()) + { + + mp_WidgetsCollection->duplicateWare(WarePath, NewWareName); + QMessageBox::warning(QApplication::activeWindow(), + tr("Ware duplication warning"), + tr("If this ware is versioned, change now the remote repository " + "(using 'git remote set-url origin '). " + "It is currently the same than original ware and would " + "generate conflict or data loss risk."), + QMessageBox::Close); + } + } + } +} + + +// ===================================================================== +// ===================================================================== + + void MainWindow::onDeleteWareRequested() { QWidget* CurrentWidget = ui->WaresTabWidget->currentWidget(); diff --git a/src/apps/openfluid-devstudio/MainWindow.hpp b/src/apps/openfluid-devstudio/MainWindow.hpp index 4e0945eec..f2af3b519 100644 --- a/src/apps/openfluid-devstudio/MainWindow.hpp +++ b/src/apps/openfluid-devstudio/MainWindow.hpp @@ -129,6 +129,8 @@ class OPENFLUID_API MainWindow: public openfluid::ui::common::AppMainWindow void updateSaveButtonsStatus(bool FileModified, bool FileOpen, bool WareModified); + void onDuplicateWareRequested(); + void onDeleteWareRequested(); void onCloseAllWaresRequested(); diff --git a/src/apps/openfluid/WareTasks.cpp b/src/apps/openfluid/WareTasks.cpp index 3494d4085..fcf81da64 100644 --- a/src/apps/openfluid/WareTasks.cpp +++ b/src/apps/openfluid/WareTasks.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -64,16 +65,28 @@ #include "DefaultDocalyzeListener.hpp" -int WareTasks::processCreate() const +void WareTasks::postWareCreation(const std::string& WarePath) const { - if (!m_Cmd.isOptionActive("id")) + if (m_Cmd.isOptionActive("set-remote")) { - return error("missing ware ID"); + if (!openfluid::utils::GitProxy::isAvailable() || openfluid::utils::GitProxy::setRemote(WarePath, + m_Cmd.getOptionValue("set-remote")) != 0) + { + openfluid::utils::log::warning("Ware creation", "set-remote failed"); + } } +} - if (!m_Cmd.isOptionActive("type")) + +// ===================================================================== +// ===================================================================== + + +int WareTasks::processCreate() const +{ + if (!m_Cmd.isOptionActive("id")) { - return error("missing ware type"); + return error("missing ware ID"); } const auto ID = m_Cmd.getOptionValue("id"); @@ -83,8 +96,6 @@ int WareTasks::processCreate() const return error("invalid ware ID"); } - const auto TypeStr = m_Cmd.getOptionValue("type"); - const auto ParentPath = (m_Cmd.getOptionValue("parent-path").empty() ? openfluid::tools::Filesystem::currentPath() : m_Cmd.getOptionValue("parent-path")); @@ -96,13 +107,29 @@ int WareTasks::processCreate() const Config.MainClassName = (m_Cmd.getOptionValue("main-class").empty() ? Config.MainClassName : m_Cmd.getOptionValue("main-class")); + if (m_Cmd.isOptionActive("from")) + { + const std::string WarePath = openfluid::waresdev::WareSrcFactory::duplicateWare(ID, ParentPath, + m_Cmd.getOptionValue("from"), + m_Cmd.isOptionActive("accept-all")); + postWareCreation(WarePath); + return 0; + } + + if (!m_Cmd.isOptionActive("type")) + { + return error("missing ware type"); + } + const auto TypeStr = m_Cmd.getOptionValue("type"); + if (TypeStr == "simulator") { openfluid::ware::SimulatorSignature Sign; Sign.ID = ID; try { - openfluid::waresdev::WareSrcFactory::createSimulator(Sign,Config,ParentPath); + const std::string WarePath = openfluid::waresdev::WareSrcFactory::createSimulator(Sign,Config,ParentPath); + postWareCreation(WarePath); return 0; } catch(const openfluid::base::FrameworkException& E) @@ -116,7 +143,8 @@ int WareTasks::processCreate() const Sign.ID = ID; try { - openfluid::waresdev::WareSrcFactory::createObserver(Sign,Config,ParentPath); + const std::string WarePath = openfluid::waresdev::WareSrcFactory::createObserver(Sign,Config,ParentPath); + postWareCreation(WarePath); return 0; } catch(const openfluid::base::FrameworkException& E) @@ -165,7 +193,8 @@ int WareTasks::processCreate() const try { - openfluid::waresdev::WareSrcFactory::createBuilderext(Sign,Config,ParentPath); + const std::string WarePath = openfluid::waresdev::WareSrcFactory::createBuilderext(Sign,Config,ParentPath); + postWareCreation(WarePath); return 0; } catch(const openfluid::base::FrameworkException& E) @@ -186,6 +215,58 @@ int WareTasks::processCreate() const // ===================================================================== +int WareTasks::processImport() const +{ + const auto ParentPath = (m_Cmd.getOptionValue("parent-path").empty() ? openfluid::tools::Filesystem::currentPath() : + m_Cmd.getOptionValue("parent-path")); + + std::string ID = m_Cmd.getOptionValue("id"); + std::string SourceType = "hub"; + std::string SourceURL = ""; + + if (m_Cmd.isOptionActive("git")) + { + SourceType = "git"; + SourceURL = m_Cmd.getOptionValue("git"); + if (m_Cmd.isOptionActive("id")) + { + ID = m_Cmd.getOptionValue("id"); + if (!openfluid::tools::isValidWareID(ID)) + { + return error("invalid ware ID"); + } + } + } + else if (m_Cmd.isOptionActive("hub")) + { + if (!m_Cmd.isOptionActive("id")) + { + return error("missing ware ID"); + } + ID = m_Cmd.getOptionValue("id"); + + if (!openfluid::tools::isValidWareID(ID)) + { + return error("invalid ware ID"); + } + if (!m_Cmd.isOptionActive("type")) + { + return error("missing ware type"); + } + SourceURL = m_Cmd.getOptionValue("hub"); + } + else + { + return error("remote URL missing: --git= or --hub[=] must be provided"); + } + return openfluid::waresdev::cloneWare(SourceURL, SourceType, ParentPath, ID, m_Cmd.getOptionValue("type")); +} + + +// ===================================================================== +// ===================================================================== + + int WareTasks::processConfigure() const { if (!openfluid::utils::CMakeProxy::isAvailable()) @@ -718,6 +799,10 @@ int WareTasks::process() const { return processCreate(); } + else if (m_Cmd.getName() == "import-ware") + { + return processImport(); + } else if (m_Cmd.getName() == "check") { return processCheck(); diff --git a/src/apps/openfluid/WareTasks.hpp b/src/apps/openfluid/WareTasks.hpp index 2cd9d2752..c98647840 100644 --- a/src/apps/openfluid/WareTasks.hpp +++ b/src/apps/openfluid/WareTasks.hpp @@ -49,8 +49,12 @@ class WareTasks : public TasksBase { private: + void postWareCreation(const std::string& WarePath) const; + int processCreate() const; + int processImport() const; + int processConfigure() const; int processBuild() const; diff --git a/src/apps/openfluid/main.cpp b/src/apps/openfluid/main.cpp index 4dca77335..5a9c72da9 100644 --- a/src/apps/openfluid/main.cpp +++ b/src/apps/openfluid/main.cpp @@ -197,6 +197,7 @@ int main(int argc, char **argv) {"id","i","ID of the ware sources to create (required)",true}, {"main-class","m","name to use for the main C++ class",true}, {"parent-path","p","parent path where to create the ware sources",true}, + {"from", "", "ware to use as reference"}, {"with-paramsui","w","generate the C++ class of the parameterization UI " "(simulators and observers only)"}, {"paramsui-class","u","name to use for the C++ class of the parameterization UI " @@ -205,11 +206,24 @@ int main(int argc, char **argv) {"bext-category","","category the Builder-Extension" "(spatial|model|results|other, default is other)",true}, {"bext-mode","","mode of Builder-Extension (modal|modeless|workspace, default is modal)", - true}}); + true}, + {"accept-all", "a", "say yes to any question (useful during ware duplication)"}, + {"set-remote", "", "define the URL of remote git repository"}}); Parser.addCommand(CreateWareCmd, &WareSection); // --- + auto ImportWareCmd = openfluid::utils::CommandLineCommand("import-ware","Import ware sources from remote git or hub"); + ImportWareCmd.addOptions({{"parent-path","p","parent path where to create the ware sources",true}, + {"git","","git URL"}, + {"hub","","hub URL"}, + {"id","i","ID of the ware sources to create"}, + {"type","t","type of the ware sources to create (simulator|observer|builderext), required " + "when importing from hub"}}); + Parser.addCommand(ImportWareCmd, &WareSection); + + // --- + auto CheckCmd = openfluid::utils::CommandLineCommand("check","Checks ware sources for potential issues"); CheckCmd.addOptions({{"src-path","s","path to the ware sources (required)",true}, {"ignore","i","ignore checks (comma separated list)"}, @@ -372,6 +386,7 @@ int main(int argc, char **argv) return ReportTasks(Parser).process(); } else if (ActiveCmdStr == "create-ware" || + ActiveCmdStr == "import-ware" || ActiveCmdStr == "check" || ActiveCmdStr == "migrate-ware" || ActiveCmdStr == "docalyze" || diff --git a/src/apps/openfluid/tests/CMakeLists.txt b/src/apps/openfluid/tests/CMakeLists.txt index b809e0de4..c28381779 100644 --- a/src/apps/openfluid/tests/CMakeLists.txt +++ b/src/apps/openfluid/tests/CMakeLists.txt @@ -393,6 +393,38 @@ ENDIF() ########################################################################### +OPENFLUID_ADD_TEST(NAME cmdline-openfluid-CreateWareSimulatorFromExisting + COMMAND "${OFBUILD_DIST_BIN_DIR}/${OPENFLUID_CMD_APP}" + "create-ware" "--id=tests.cmdline.sim.ilar" + "-p" "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim" + "--from=${OFBUILD_TESTS_OUTPUT_DATA_DIR}/cmdline/create-ware/sim/tests.cmdline.sim" + "--set-remote=https://hub.foo.org/git-service/wares/simulators/tests.cmdline.sim.ilar" + "-a" + PRE_TEST REMOVE_DIRECTORY "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim/tests.cmdline.sim.ilar" + POST_TEST CHECK_FILE_EXIST "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim/tests.cmdline.sim.ilar/CMakeLists.txt" + POST_TEST CHECK_FILE_CONTAINS "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim/tests.cmdline.sim.ilar/openfluid-ware.json" "tests.cmdline.sim.ilar" + POST_TEST CHECK_FILE_CONTAINS "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim/tests.cmdline.sim.ilar/.git/config" "hub.foo.org") + +SET_PROPERTY(TEST cmdline-openfluid-CreateWareSimulatorFromExisting APPEND PROPERTY DEPENDS cmdline-openfluid-CreateWareSimulator) + + +########################################################################### + + +OPENFLUID_ADD_TEST(NAME cmdline-openfluid-ImportSimulator + COMMAND "${OFBUILD_DIST_BIN_DIR}/${OPENFLUID_CMD_APP}" + "import-ware" + "-p" "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim" + "--id=tests.cmdline.sim.ported" + "--git=${OFBUILD_TESTS_GITHELPER_URL}" + PRE_TEST REMOVE_DIRECTORY "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim/tests.cmdline.sim.ported" + PRE_TEST CREATE_DIRECTORY "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim" + POST_TEST CHECK_FILE_EXIST "${OFBUILD_TMP_DIR}/cmdline/create-ware/sim/tests.cmdline.sim.ported/master.txt") + + +########################################################################### + + OPENFLUID_ADD_TEST(NAME cmdline-openfluid-CreateWareObserver COMMAND "${OFBUILD_DIST_BIN_DIR}/${OPENFLUID_CMD_APP}" "create-ware" "--type=observer" "--id=tests.cmdline.obs" @@ -555,7 +587,11 @@ OPENFLUID_ADD_TEST(NAME cmdline-openfluid-MigrateSimulator POST_TEST CHECK_FILE_EXIST "${OFBUILD_TMP_DIR}/cmdline/migrate-ware/sim/tests.cmdline.sim-migration/src/CMakeLists.txt" POST_TEST CHECK_FILE_IDENTICAL "${OFBUILD_TMP_DIR}/cmdline/migrate-ware/sim/tests.cmdline.sim-migration/" "${OFBUILD_TMP_DIR}/cmdline/migrate-ware/sim/tests.cmdline.sim-migration/src/REF/" - "openfluid-ware.json") + "openfluid-ware.json" + POST_TEST CHECK_FILE_CONTAINS "${OFBUILD_TMP_DIR}/cmdline/migrate-ware/sim/tests.cmdline.sim-migration/src/CMakeLists.txt" + "SET(CUSTOM_VAR foo)" + POST_TEST CHECK_FILE_CONTAINS "${OFBUILD_TMP_DIR}/cmdline/migrate-ware/sim/tests.cmdline.sim-migration/src/CMakeLists.txt" + "MESSAGE(STATUS \"custom message\")") SET_PROPERTY(TEST cmdline-openfluid-MigrateSimulator APPEND PROPERTY ENVIRONMENT "OpenFLUID_DIR=${OFBUILD_DIST_CMAKE_MODULES_DIR};OpenFLUID_EXTRA_SEARCH_PATHS=${CMAKE_BINARY_DIR}/src\;${OFBUILD_DIST_DIR};OpenFLUID_MIGRATION_INCLUDE_PATHS=${CMAKE_SOURCE_DIR}/src") set_tests_properties(cmdline-openfluid-MigrateSimulator PROPERTIES FIXTURES_SETUP SimulatorMigrated) diff --git a/src/observers/CMakeLists.txt b/src/observers/CMakeLists.txt index c2c374041..f37a1936c 100644 --- a/src/observers/CMakeLists.txt +++ b/src/observers/CMakeLists.txt @@ -10,3 +10,11 @@ OFBUILD_ADD_OBSERVER(export.spatial-graph.files.dot) INSTALL(DIRECTORY "${OFBUILD_DIST_OBSERVERS_DIR}/" DESTINATION "${OPENFLUID_OBSERVERS_INSTALL_PATH}") + +SET(OFBUILD_SOURCE_OBSERVERS_DIR "${CMAKE_SOURCE_DIR}/src/${OPENFLUID_OBSERVERS_PATH}") + +FILE(COPY "${OFBUILD_SOURCE_OBSERVERS_DIR}" + DESTINATION "${OFBUILD_DIST_DIR}/${OPENFLUID_EXAMPLES_WARESDEV_INSTALL_PATH}") + +INSTALL(DIRECTORY "${OFBUILD_DIST_DIR}/${OPENFLUID_EXAMPLES_WARESDEV_INSTALL_PATH}" + DESTINATION "${OPENFLUID_DOC_INSTALL_PATH}/") diff --git a/src/observers/export.spatial-graph.files.dot/src/CMakeLists.txt b/src/observers/export.spatial-graph.files.dot/src/CMakeLists.txt index e9ef848dc..bb39ce434 100644 --- a/src/observers/export.spatial-graph.files.dot/src/CMakeLists.txt +++ b/src/observers/export.spatial-graph.files.dot/src/CMakeLists.txt @@ -21,7 +21,7 @@ OPENFLUID_ADD_WAREPLUGIN( # list of C++ files - CPP_FILES DotFilesObs.cpp + CPP_FILES WareMain.cpp # list of Fortran files #FORTRAN_FILES # list of UI files (mainly for builderexts) diff --git a/src/observers/export.spatial-graph.files.dot/src/DotFilesObs.cpp b/src/observers/export.spatial-graph.files.dot/src/WareMain.cpp similarity index 99% rename from src/observers/export.spatial-graph.files.dot/src/DotFilesObs.cpp rename to src/observers/export.spatial-graph.files.dot/src/WareMain.cpp index 82f8e1fab..2676b6a25 100644 --- a/src/observers/export.spatial-graph.files.dot/src/DotFilesObs.cpp +++ b/src/observers/export.spatial-graph.files.dot/src/WareMain.cpp @@ -31,7 +31,7 @@ /** - @file DotFilesObs.cpp + @file WareMain.cpp @author Jean-Christophe FABRE */ diff --git a/src/observers/export.vars.files.csv-multicols/src/CMakeLists.txt b/src/observers/export.vars.files.csv-multicols/src/CMakeLists.txt index 9daf6434d..5371c753d 100644 --- a/src/observers/export.vars.files.csv-multicols/src/CMakeLists.txt +++ b/src/observers/export.vars.files.csv-multicols/src/CMakeLists.txt @@ -22,7 +22,7 @@ add_compile_definitions(QT_VERSION_MAJOR=${QT_VERSION_MAJOR}) OPENFLUID_ADD_WAREPLUGIN( # list of C++ files - CPP_FILES MultiCSVFilesObs.cpp MultiCSVObsTools.cpp + CPP_FILES WareMain.cpp MultiCSVObsTools.cpp # list of Fortran files #FORTRAN_FILES # list of UI files (mainly for builderexts) @@ -42,9 +42,9 @@ OPENFLUID_ADD_WAREPLUGIN( # enable build of parameterization UI (ON/OFF, OFF is default) WITH_PARAMSUI ON # list of C++ files for parametrization UI - PARAMSUI_CPP_FILES MultiEditFormatDialog.cpp MultiEditSetDialog.cpp MultiCSVObsParamsWidget.cpp MultiCSVObsTools.cpp EditClassIDVarDialog.cpp + PARAMSUI_CPP_FILES ui/MultiEditFormatDialog.cpp ui/MultiEditSetDialog.cpp ui/MultiCSVObsParamsWidget.cpp MultiCSVObsTools.cpp ui/EditClassIDVarDialog.cpp # list of UI files for parametrization UI - PARAMSUI_UI_FILES MultiEditFormatDialog.ui MultiEditSetDialog.ui MultiCSVObsParamsWidget.ui EditClassIDVarDialog.ui + PARAMSUI_UI_FILES ui/MultiEditFormatDialog.ui ui/MultiEditSetDialog.ui ui/MultiCSVObsParamsWidget.ui ui/EditClassIDVarDialog.ui # list of RC files for parametrization UI #PARAMSUI_RC_FILES # list of languages for translation of parametrization UI diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiCSVObsTools.hpp b/src/observers/export.vars.files.csv-multicols/src/MultiCSVObsTools.hpp index ce3f84c49..c1783d9b2 100644 --- a/src/observers/export.vars.files.csv-multicols/src/MultiCSVObsTools.hpp +++ b/src/observers/export.vars.files.csv-multicols/src/MultiCSVObsTools.hpp @@ -43,12 +43,12 @@ #include #include +#include + #include #include #include -#include "../../CsvObserverBase.hpp" - // ===================================================================== // ===================================================================== diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiCSVFilesObs.cpp b/src/observers/export.vars.files.csv-multicols/src/WareMain.cpp similarity index 97% rename from src/observers/export.vars.files.csv-multicols/src/MultiCSVFilesObs.cpp rename to src/observers/export.vars.files.csv-multicols/src/WareMain.cpp index e23e0d9f6..98678dec8 100644 --- a/src/observers/export.vars.files.csv-multicols/src/MultiCSVFilesObs.cpp +++ b/src/observers/export.vars.files.csv-multicols/src/WareMain.cpp @@ -31,7 +31,7 @@ /** - @file MultiCSVFilesObs.cpp + @file WareMain.cpp @author Armel THONI */ @@ -85,6 +85,8 @@ class CSVMultiColFilesObserver : public CSVFilesObserverBase bool m_KeepNA; + bool m_FilesInitialized = false; + public: @@ -288,7 +290,8 @@ class CSVMultiColFilesObserver : public CSVFilesObserverBase SetFiles.second.File = new BaseCSVFile(); SetFiles.second.ColumnsHeaders = ColumnsHeaders; } - + + m_FilesInitialized = true; for (auto& SetFiles : m_SetsFiles) { @@ -368,12 +371,15 @@ class CSVMultiColFilesObserver : public CSVFilesObserverBase void onFinalizedRun() { - for (auto& SetFiles : m_SetsFiles) + if (m_FilesInitialized) { - if(SetFiles.second.File) + for (auto& SetFiles : m_SetsFiles) { - delete SetFiles.second.File; - SetFiles.second.File = 0; // avoid double delete issues + if(SetFiles.second.File) + { + delete SetFiles.second.File; + SetFiles.second.File = 0; // avoid double delete issues + } } } } diff --git a/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/CMakeLists.txt b/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/CMakeLists.txt new file mode 100644 index 000000000..73e0d1f1d --- /dev/null +++ b/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/CMakeLists.txt @@ -0,0 +1,6 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.10) + +PROJECT("observer.csv.base") + +FIND_PACKAGE(OpenFLUID COMPONENTS core tools ware REQUIRED) + diff --git a/src/observers/CsvObserverBase.hpp b/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/CsvObserverBase.hpp similarity index 99% rename from src/observers/CsvObserverBase.hpp rename to src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/CsvObserverBase.hpp index 21166ee0f..d1801240f 100644 --- a/src/observers/CsvObserverBase.hpp +++ b/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/CsvObserverBase.hpp @@ -45,6 +45,7 @@ #include #include +#include #include #include diff --git a/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/README.md b/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/README.md new file mode 100644 index 000000000..ef2818bba --- /dev/null +++ b/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/README.md @@ -0,0 +1 @@ +# observer.csv.base fragment diff --git a/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/openfluid-fragment.json b/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/openfluid-fragment.json new file mode 100644 index 000000000..bc3a56e07 --- /dev/null +++ b/src/observers/export.vars.files.csv-multicols/src/fragments/observer.csv.base/openfluid-fragment.json @@ -0,0 +1,23 @@ +{ + "id": "observer.csv.base", + "name": "", + "description": "", + "version": "0.1", + "status": "alpha", + "authors": [ + { + "name": "Armel Thöni", + "email": "armel.thoni@inrae.fr" + } + ], + "contacts": [], + "license": "", + "tags": [], + "links": [], + "issues": [], + "dependencies": {}, + "fragment": { + "openfluid-components": ["core", "tools", "ware"] + } +} + diff --git a/src/observers/export.vars.files.csv-multicols/src/EditClassIDVarDialog.cpp b/src/observers/export.vars.files.csv-multicols/src/ui/EditClassIDVarDialog.cpp similarity index 100% rename from src/observers/export.vars.files.csv-multicols/src/EditClassIDVarDialog.cpp rename to src/observers/export.vars.files.csv-multicols/src/ui/EditClassIDVarDialog.cpp diff --git a/src/observers/export.vars.files.csv-multicols/src/EditClassIDVarDialog.hpp b/src/observers/export.vars.files.csv-multicols/src/ui/EditClassIDVarDialog.hpp similarity index 98% rename from src/observers/export.vars.files.csv-multicols/src/EditClassIDVarDialog.hpp rename to src/observers/export.vars.files.csv-multicols/src/ui/EditClassIDVarDialog.hpp index 3278a5f3d..184958cd9 100644 --- a/src/observers/export.vars.files.csv-multicols/src/EditClassIDVarDialog.hpp +++ b/src/observers/export.vars.files.csv-multicols/src/ui/EditClassIDVarDialog.hpp @@ -43,7 +43,7 @@ #include #include -#include "MultiCSVObsTools.hpp" +#include "../MultiCSVObsTools.hpp" namespace Ui diff --git a/src/observers/export.vars.files.csv-multicols/src/EditClassIDVarDialog.ui b/src/observers/export.vars.files.csv-multicols/src/ui/EditClassIDVarDialog.ui similarity index 100% rename from src/observers/export.vars.files.csv-multicols/src/EditClassIDVarDialog.ui rename to src/observers/export.vars.files.csv-multicols/src/ui/EditClassIDVarDialog.ui diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiCSVObsParamsWidget.cpp b/src/observers/export.vars.files.csv-multicols/src/ui/MultiCSVObsParamsWidget.cpp similarity index 100% rename from src/observers/export.vars.files.csv-multicols/src/MultiCSVObsParamsWidget.cpp rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiCSVObsParamsWidget.cpp diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiCSVObsParamsWidget.hpp b/src/observers/export.vars.files.csv-multicols/src/ui/MultiCSVObsParamsWidget.hpp similarity index 98% rename from src/observers/export.vars.files.csv-multicols/src/MultiCSVObsParamsWidget.hpp rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiCSVObsParamsWidget.hpp index 83c23af8c..feb1bcaf0 100644 --- a/src/observers/export.vars.files.csv-multicols/src/MultiCSVObsParamsWidget.hpp +++ b/src/observers/export.vars.files.csv-multicols/src/ui/MultiCSVObsParamsWidget.hpp @@ -43,7 +43,7 @@ #include -#include "MultiCSVObsTools.hpp" +#include "../MultiCSVObsTools.hpp" namespace Ui diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiCSVObsParamsWidget.ui b/src/observers/export.vars.files.csv-multicols/src/ui/MultiCSVObsParamsWidget.ui similarity index 100% rename from src/observers/export.vars.files.csv-multicols/src/MultiCSVObsParamsWidget.ui rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiCSVObsParamsWidget.ui diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiEditFormatDialog.cpp b/src/observers/export.vars.files.csv-multicols/src/ui/MultiEditFormatDialog.cpp similarity index 100% rename from src/observers/export.vars.files.csv-multicols/src/MultiEditFormatDialog.cpp rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiEditFormatDialog.cpp diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiEditFormatDialog.hpp b/src/observers/export.vars.files.csv-multicols/src/ui/MultiEditFormatDialog.hpp similarity index 98% rename from src/observers/export.vars.files.csv-multicols/src/MultiEditFormatDialog.hpp rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiEditFormatDialog.hpp index 77f5ed79c..1fc62ccf6 100644 --- a/src/observers/export.vars.files.csv-multicols/src/MultiEditFormatDialog.hpp +++ b/src/observers/export.vars.files.csv-multicols/src/ui/MultiEditFormatDialog.hpp @@ -44,7 +44,7 @@ #include #include -#include "MultiCSVObsTools.hpp" +#include "../MultiCSVObsTools.hpp" namespace Ui diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiEditFormatDialog.ui b/src/observers/export.vars.files.csv-multicols/src/ui/MultiEditFormatDialog.ui similarity index 100% rename from src/observers/export.vars.files.csv-multicols/src/MultiEditFormatDialog.ui rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiEditFormatDialog.ui diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiEditSetDialog.cpp b/src/observers/export.vars.files.csv-multicols/src/ui/MultiEditSetDialog.cpp similarity index 100% rename from src/observers/export.vars.files.csv-multicols/src/MultiEditSetDialog.cpp rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiEditSetDialog.cpp diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiEditSetDialog.hpp b/src/observers/export.vars.files.csv-multicols/src/ui/MultiEditSetDialog.hpp similarity index 98% rename from src/observers/export.vars.files.csv-multicols/src/MultiEditSetDialog.hpp rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiEditSetDialog.hpp index f4cddee48..34dcd6f77 100644 --- a/src/observers/export.vars.files.csv-multicols/src/MultiEditSetDialog.hpp +++ b/src/observers/export.vars.files.csv-multicols/src/ui/MultiEditSetDialog.hpp @@ -47,7 +47,7 @@ #include #include -#include "MultiCSVObsTools.hpp" +#include "../MultiCSVObsTools.hpp" namespace Ui diff --git a/src/observers/export.vars.files.csv-multicols/src/MultiEditSetDialog.ui b/src/observers/export.vars.files.csv-multicols/src/ui/MultiEditSetDialog.ui similarity index 100% rename from src/observers/export.vars.files.csv-multicols/src/MultiEditSetDialog.ui rename to src/observers/export.vars.files.csv-multicols/src/ui/MultiEditSetDialog.ui diff --git a/src/observers/export.vars.files.csv/src/CMakeLists.txt b/src/observers/export.vars.files.csv/src/CMakeLists.txt index 1d1fd7b84..7b5bdc775 100644 --- a/src/observers/export.vars.files.csv/src/CMakeLists.txt +++ b/src/observers/export.vars.files.csv/src/CMakeLists.txt @@ -22,7 +22,7 @@ add_compile_definitions(QT_VERSION_MAJOR=${QT_VERSION_MAJOR}) OPENFLUID_ADD_WAREPLUGIN( # list of C++ files - CPP_FILES CSVFilesObs.cpp CSVObsTools.cpp + CPP_FILES WareMain.cpp CSVObsTools.cpp # list of Fortran files #FORTRAN_FILES # list of UI files (mainly for builderexts) @@ -42,9 +42,9 @@ OPENFLUID_ADD_WAREPLUGIN( # enable build of parameterization UI (ON/OFF, OFF is default) WITH_PARAMSUI ON # list of C++ files for parametrization UI - PARAMSUI_CPP_FILES CSVObsParamsWidget.cpp EditFormatDialog.cpp EditSetDialog.cpp CSVObsTools.cpp + PARAMSUI_CPP_FILES ui/CSVObsParamsWidget.cpp ui/EditFormatDialog.cpp ui/EditSetDialog.cpp CSVObsTools.cpp # list of UI files for parametrization UI - PARAMSUI_UI_FILES CSVObsParamsWidget.ui EditFormatDialog.ui EditSetDialog.ui + PARAMSUI_UI_FILES ui/CSVObsParamsWidget.ui ui/EditFormatDialog.ui ui/EditSetDialog.ui # list of RC files for parametrization UI #PARAMSUI_RC_FILES # list of languages for translation of parametrization UI diff --git a/src/observers/export.vars.files.csv/src/CSVObsTools.hpp b/src/observers/export.vars.files.csv/src/CSVObsTools.hpp index 02e852347..a47ddfb0b 100644 --- a/src/observers/export.vars.files.csv/src/CSVObsTools.hpp +++ b/src/observers/export.vars.files.csv/src/CSVObsTools.hpp @@ -43,11 +43,11 @@ #include #include +#include + #include #include -#include "../../CsvObserverBase.hpp" - // ===================================================================== // ===================================================================== diff --git a/src/observers/export.vars.files.csv/src/CSVFilesObs.cpp b/src/observers/export.vars.files.csv/src/WareMain.cpp similarity index 99% rename from src/observers/export.vars.files.csv/src/CSVFilesObs.cpp rename to src/observers/export.vars.files.csv/src/WareMain.cpp index f0d4b5509..94850e49d 100644 --- a/src/observers/export.vars.files.csv/src/CSVFilesObs.cpp +++ b/src/observers/export.vars.files.csv/src/WareMain.cpp @@ -31,7 +31,7 @@ /** - @file CSVFilesObs.cpp + @file WareMain.cpp @author Jean-Christophe FABRE */ diff --git a/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/CMakeLists.txt b/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/CMakeLists.txt new file mode 100644 index 000000000..73e0d1f1d --- /dev/null +++ b/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/CMakeLists.txt @@ -0,0 +1,6 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.10) + +PROJECT("observer.csv.base") + +FIND_PACKAGE(OpenFLUID COMPONENTS core tools ware REQUIRED) + diff --git a/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/CsvObserverBase.hpp b/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/CsvObserverBase.hpp new file mode 100644 index 000000000..d1801240f --- /dev/null +++ b/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/CsvObserverBase.hpp @@ -0,0 +1,426 @@ +/* + + This file is part of OpenFLUID software + Copyright(c) 2007, INRA - Montpellier SupAgro + + + == GNU General Public License Usage == + + OpenFLUID is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFLUID is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenFLUID. If not, see . + + + == Other Usage == + + Other Usage means a use of OpenFLUID that is inconsistent with the GPL + license, and requires a written agreement between You and INRA. + Licensees for Other Usage of OpenFLUID may use this file in accordance + with the terms contained in the written agreement between You and INRA. + +*/ + + +/** + @file CsvObserverBase.hpp + + @author Armel THONI +*/ + +#ifndef __CSVOBSERVERBASE_HPP__ +#define __CSVOBSERVERBASE_HPP__ + + +#include +#include +#include + +#include +#include +#include +#include + + +constexpr const char* CSV_FILES_EXT = "csv"; + + +// ===================================================================== +// ===================================================================== + + +inline void setStreamFormat(std::ostream& Stream, const unsigned int Precision, const std::string& FloatFormat) +{ + // set precision + if (FloatFormat == "fixed") + { + Stream << std::fixed; + } + else if (FloatFormat == "scientific") + { + Stream << std::scientific; + } + else + { + Stream << std::defaultfloat; + } + + Stream << std::setprecision(Precision); +} + + +// ===================================================================== +// ===================================================================== + + +class CSVFormat +{ + public: + + enum class HeaderType { None, Info, ColnamesAsData, ColnamesAsComment, Full }; + + HeaderType Header; + + std::string ColSeparator; + + std::string DateFormat; + + std::string CommentChar; + + unsigned int Precision; + + std::string FloatFormat; + + bool IsTimeIndexDateFormat; + + CSVFormat() : Header(HeaderType::Info), ColSeparator(";"), DateFormat("%Y%m%dT%H%M%S"), + CommentChar("#"), Precision(5), FloatFormat("default"), IsTimeIndexDateFormat(false) + { + + } + + void adaptStreamFormat(std::ostream& Stream) + { + setStreamFormat(Stream, Precision, FloatFormat); + } + + std::vector generateFormatFields(const std::string& FormatName); + +}; + + +// ===================================================================== +// ===================================================================== + + +class BaseCSVFile +{ + public: + + char* FileBuffer; + + std::ofstream FileHandle; + + std::string FileName; + + BaseCSVFile() : + FileBuffer(nullptr), FileHandle() + { } + + ~BaseCSVFile() + { + if (FileHandle.is_open()) + { + FileHandle.close(); + } + delete [] FileBuffer; + } +}; + + +// ===================================================================== +// ===================================================================== + + +class CSVFilesObserverBase : public openfluid::ware::PluggableObserver +{ + protected: + + std::string m_OutputDir; + + unsigned int m_BufferSize; + + std::string m_OutFileExt; + + + public: + + CSVFilesObserverBase() : PluggableObserver(), + m_OutputDir(""), m_BufferSize(2*1024), m_OutFileExt(CSV_FILES_EXT) + { + + } + + + // ===================================================================== + // ===================================================================== + + + void onInitializedRun() + { + saveToFiles(); + } + + + // ===================================================================== + // ===================================================================== + + + void onStepCompleted() + { + saveToFiles(); + } + + + // ===================================================================== + // ===================================================================== + + + virtual void saveToFiles()=0; +}; + + +// ===================================================================== +// ===================================================================== + + +inline CSVFormat::HeaderType StrToHeaderType(const std::string& HeaderStr) +{ + if (HeaderStr == "none") + { + return CSVFormat::HeaderType::None; + } + else if (HeaderStr == "colnames-as-data") + { + return CSVFormat::HeaderType::ColnamesAsData; + } + else if (HeaderStr == "colnames-as-comment") + { + return CSVFormat::HeaderType::ColnamesAsComment; + } + else if (HeaderStr == "full") + { + return CSVFormat::HeaderType::Full; + } + else + { + return CSVFormat::HeaderType::Info; + } +} + + +// ===================================================================== +// ===================================================================== + + +inline std::string HeaderTypeToStr(CSVFormat::HeaderType HType) +{ + if (HType == CSVFormat::HeaderType::ColnamesAsComment) + { + return "colnames-as-comment"; + } + else if (HType == CSVFormat::HeaderType::ColnamesAsData) + { + return "colnames-as-data"; + } + else if (HType == CSVFormat::HeaderType::Full) + { + return "full"; + } + else if (HType == CSVFormat::HeaderType::None) + { + return "none"; + } + else + { + return ""; + } +} + + +// ===================================================================== +// ===================================================================== + + +inline std::string StrToDateFormat(const std::string& FormatStr) +{ + std::string UpperFormatStr = FormatStr; + std::for_each(UpperFormatStr.begin(), UpperFormatStr.end(), [](char & c){c = ::toupper(c);}); + + if (UpperFormatStr == "ISO") + { + return "%Y%m%dT%H%M%S"; + } + else if (UpperFormatStr == "ISOEXT") + { + return "%Y-%m-%dT%H:%M:%S"; + } + else if (UpperFormatStr == "6COLS") + { + return "%Y\t%m\t%d\t%H\t%M\t%S"; + } + else + { + return FormatStr; + } +} + + + // ===================================================================== + // ===================================================================== + + +typedef std::map FormatMap_t; + + +// ===================================================================== +// ===================================================================== + + +inline std::string buildTimeHeader(const CSVFormat& Format, const openfluid::core::VariableName_t& VarName) +{ + std::ostringstream HeaderSStr; + if(Format.Header == CSVFormat::HeaderType::ColnamesAsComment || Format.Header == CSVFormat::HeaderType::Full) + { + if (Format.IsTimeIndexDateFormat) + { + HeaderSStr << Format.CommentChar << "timeindex"; + } + else + { + HeaderSStr << Format.CommentChar << "datetime"; + } + + HeaderSStr << Format.ColSeparator << VarName << "\n"; + } + + if(Format.Header == CSVFormat::HeaderType::ColnamesAsData) + { + if (Format.IsTimeIndexDateFormat) + { + HeaderSStr << "timeindex"; + } + else + { + HeaderSStr << "datetime"; + } + + HeaderSStr << Format.ColSeparator << VarName << "\n"; + } + + return HeaderSStr.str(); +} + + +// ===================================================================== +// ===================================================================== + + +inline openfluid::core::StringValue basicParseFormatsFromParamsTree( + const openfluid::core::Tree& Format, + const std::string& Key) +{ + if (Key == "colsep") + { + return Format.getChildValue(Key,"\t"); + } + else if (Key == "precision") + { + return Format.getChildValue(Key,5); + } + else if (Key == "float-format") + { + return Format.getChildValue(Key,"auto"); + } + else if (Key == "date") + { + return Format.getChildValue(Key,"ISO"); + } + else if (Key == "commentchar") + { + return Format.getChildValue(Key, "#"); + } + else if (Key == "header") + { + return Format.getChildValue(Key, ""); + } + else + { + throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION, + "Key for basic parsing not found " + Key); + } +} + + +// ===================================================================== +// ===================================================================== + + +inline std::vector CSVFormat::generateFormatFields(const std::string& FormatName) +{ + std::string VisibleColSeparator = ColSeparator; + VisibleColSeparator = openfluid::tools::replace(VisibleColSeparator, "\t", "\\t"); + return { + FormatName, + VisibleColSeparator, + DateFormat, + std::to_string(Precision), + FloatFormat, + HeaderTypeToStr(Header), + CommentChar + }; +} + + +// ===================================================================== +// ===================================================================== + + +inline std::vector getPreviewDateTimes() +{ + return { + openfluid::core::DateTime(2010,7,30,16,30,0), + openfluid::core::DateTime(2010,7,30,16,45,18), + openfluid::core::DateTime(2010,7,30,17,52,22), + openfluid::core::DateTime(2010,7,30,18,0,00), + openfluid::core::DateTime(2010,7,30,19,0,0) + }; +} + + +// ===================================================================== +// ===================================================================== + + +inline std::vector getPreviewValues(bool missing=false) +{ + if (missing) + { + return {3.896554654, 19.2, 0.0, 0.000000523}; + } + else + { + return {1.5, 3.896554654, 19.2, 0.0, 0.000000523}; + } +} + + +#endif /* __CSVOBSERVERBASE_HPP__ */ diff --git a/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/README.md b/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/README.md new file mode 100644 index 000000000..ef2818bba --- /dev/null +++ b/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/README.md @@ -0,0 +1 @@ +# observer.csv.base fragment diff --git a/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/openfluid-fragment.json b/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/openfluid-fragment.json new file mode 100644 index 000000000..bc3a56e07 --- /dev/null +++ b/src/observers/export.vars.files.csv/src/fragments/observer.csv.base/openfluid-fragment.json @@ -0,0 +1,23 @@ +{ + "id": "observer.csv.base", + "name": "", + "description": "", + "version": "0.1", + "status": "alpha", + "authors": [ + { + "name": "Armel Thöni", + "email": "armel.thoni@inrae.fr" + } + ], + "contacts": [], + "license": "", + "tags": [], + "links": [], + "issues": [], + "dependencies": {}, + "fragment": { + "openfluid-components": ["core", "tools", "ware"] + } +} + diff --git a/src/observers/export.vars.files.csv/src/CSVObsParamsWidget.cpp b/src/observers/export.vars.files.csv/src/ui/CSVObsParamsWidget.cpp similarity index 100% rename from src/observers/export.vars.files.csv/src/CSVObsParamsWidget.cpp rename to src/observers/export.vars.files.csv/src/ui/CSVObsParamsWidget.cpp diff --git a/src/observers/export.vars.files.csv/src/CSVObsParamsWidget.hpp b/src/observers/export.vars.files.csv/src/ui/CSVObsParamsWidget.hpp similarity index 98% rename from src/observers/export.vars.files.csv/src/CSVObsParamsWidget.hpp rename to src/observers/export.vars.files.csv/src/ui/CSVObsParamsWidget.hpp index e205b9227..decdc7231 100644 --- a/src/observers/export.vars.files.csv/src/CSVObsParamsWidget.hpp +++ b/src/observers/export.vars.files.csv/src/ui/CSVObsParamsWidget.hpp @@ -43,7 +43,7 @@ #include -#include "CSVObsTools.hpp" +#include "../CSVObsTools.hpp" namespace Ui diff --git a/src/observers/export.vars.files.csv/src/CSVObsParamsWidget.ui b/src/observers/export.vars.files.csv/src/ui/CSVObsParamsWidget.ui similarity index 100% rename from src/observers/export.vars.files.csv/src/CSVObsParamsWidget.ui rename to src/observers/export.vars.files.csv/src/ui/CSVObsParamsWidget.ui diff --git a/src/observers/export.vars.files.csv/src/EditFormatDialog.cpp b/src/observers/export.vars.files.csv/src/ui/EditFormatDialog.cpp similarity index 100% rename from src/observers/export.vars.files.csv/src/EditFormatDialog.cpp rename to src/observers/export.vars.files.csv/src/ui/EditFormatDialog.cpp diff --git a/src/observers/export.vars.files.csv/src/EditFormatDialog.hpp b/src/observers/export.vars.files.csv/src/ui/EditFormatDialog.hpp similarity index 98% rename from src/observers/export.vars.files.csv/src/EditFormatDialog.hpp rename to src/observers/export.vars.files.csv/src/ui/EditFormatDialog.hpp index 001c5fd9b..0948dff24 100644 --- a/src/observers/export.vars.files.csv/src/EditFormatDialog.hpp +++ b/src/observers/export.vars.files.csv/src/ui/EditFormatDialog.hpp @@ -44,7 +44,7 @@ #include #include -#include "CSVObsTools.hpp" +#include "../CSVObsTools.hpp" namespace Ui diff --git a/src/observers/export.vars.files.csv/src/EditFormatDialog.ui b/src/observers/export.vars.files.csv/src/ui/EditFormatDialog.ui similarity index 100% rename from src/observers/export.vars.files.csv/src/EditFormatDialog.ui rename to src/observers/export.vars.files.csv/src/ui/EditFormatDialog.ui diff --git a/src/observers/export.vars.files.csv/src/EditSetDialog.cpp b/src/observers/export.vars.files.csv/src/ui/EditSetDialog.cpp similarity index 100% rename from src/observers/export.vars.files.csv/src/EditSetDialog.cpp rename to src/observers/export.vars.files.csv/src/ui/EditSetDialog.cpp diff --git a/src/observers/export.vars.files.csv/src/EditSetDialog.hpp b/src/observers/export.vars.files.csv/src/ui/EditSetDialog.hpp similarity index 100% rename from src/observers/export.vars.files.csv/src/EditSetDialog.hpp rename to src/observers/export.vars.files.csv/src/ui/EditSetDialog.hpp diff --git a/src/observers/export.vars.files.csv/src/EditSetDialog.ui b/src/observers/export.vars.files.csv/src/ui/EditSetDialog.ui similarity index 100% rename from src/observers/export.vars.files.csv/src/EditSetDialog.ui rename to src/observers/export.vars.files.csv/src/ui/EditSetDialog.ui diff --git a/src/observers/export.vars.files.geovector/src/CMakeLists.txt b/src/observers/export.vars.files.geovector/src/CMakeLists.txt index 47821678c..da538d152 100644 --- a/src/observers/export.vars.files.geovector/src/CMakeLists.txt +++ b/src/observers/export.vars.files.geovector/src/CMakeLists.txt @@ -23,7 +23,7 @@ SET(WARE_LINK_LIBS ${GDAL_LIBRARIES}) OPENFLUID_ADD_WAREPLUGIN( # list of C++ files - CPP_FILES GeoVectorFilesObs.cpp + CPP_FILES WareMain.cpp # list of Fortran files #FORTRAN_FILES # list of UI files (mainly for builderexts) diff --git a/src/observers/export.vars.files.geovector/src/GeoVectorFilesObs.cpp b/src/observers/export.vars.files.geovector/src/WareMain.cpp similarity index 99% rename from src/observers/export.vars.files.geovector/src/GeoVectorFilesObs.cpp rename to src/observers/export.vars.files.geovector/src/WareMain.cpp index 17c15ae8b..5b068ce65 100644 --- a/src/observers/export.vars.files.geovector/src/GeoVectorFilesObs.cpp +++ b/src/observers/export.vars.files.geovector/src/WareMain.cpp @@ -31,7 +31,7 @@ /** - @file GeoVectorFilesObs.cpp + @file WareMain.cpp @author Jean-Christophe FABRE */ diff --git a/src/observers/export.vars.files.kml-anim/src/CMakeLists.txt b/src/observers/export.vars.files.kml-anim/src/CMakeLists.txt index beffb58e2..9259447b2 100644 --- a/src/observers/export.vars.files.kml-anim/src/CMakeLists.txt +++ b/src/observers/export.vars.files.kml-anim/src/CMakeLists.txt @@ -23,7 +23,7 @@ SET(WARE_LINK_LIBS ${GDAL_LIBRARIES}) OPENFLUID_ADD_WAREPLUGIN( # list of C++ files - CPP_FILES KmlFilesAnimObs.cpp + CPP_FILES WareMain.cpp # list of Fortran files #FORTRAN_FILES # list of UI files (mainly for builderexts) diff --git a/src/observers/export.vars.files.kml-anim/src/KmlFilesAnimObs.cpp b/src/observers/export.vars.files.kml-anim/src/WareMain.cpp similarity index 99% rename from src/observers/export.vars.files.kml-anim/src/KmlFilesAnimObs.cpp rename to src/observers/export.vars.files.kml-anim/src/WareMain.cpp index 09a80a014..664686846 100644 --- a/src/observers/export.vars.files.kml-anim/src/KmlFilesAnimObs.cpp +++ b/src/observers/export.vars.files.kml-anim/src/WareMain.cpp @@ -31,7 +31,7 @@ /** - @file KmlFilesAnimObs.cpp + @file WareMain.cpp @author Jean-Christophe FABRE */ @@ -42,11 +42,11 @@ #include #include +#include + #include #include -#include "../../KmlObserverBase.hpp" - class KmlAnimLayerInfo : public KmlLayerInfo { diff --git a/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/CMakeLists.txt b/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/CMakeLists.txt new file mode 100644 index 000000000..140b9bfa6 --- /dev/null +++ b/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/CMakeLists.txt @@ -0,0 +1,5 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.10) + +PROJECT("observer.kml.base") + +FIND_PACKAGE(OpenFLUID COMPONENTS tools utils ware REQUIRED) diff --git a/src/observers/KmlObserverBase.hpp b/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/KmlObserverBase.hpp similarity index 100% rename from src/observers/KmlObserverBase.hpp rename to src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/KmlObserverBase.hpp diff --git a/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/README.md b/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/README.md new file mode 100644 index 000000000..379518693 --- /dev/null +++ b/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/README.md @@ -0,0 +1 @@ +# observer.kml.base fragment \ No newline at end of file diff --git a/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/openfluid-fragment.json b/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/openfluid-fragment.json new file mode 100644 index 000000000..85550c457 --- /dev/null +++ b/src/observers/export.vars.files.kml-anim/src/fragments/observer.kml.base/openfluid-fragment.json @@ -0,0 +1,22 @@ +{ + "id": "observer.kml.base", + "name": "", + "description": "", + "version": "0.1", + "status": "alpha", + "authors": [ + { + "name": "Jean-Christophe Fabre", + "email": "jean-christophe.fabre@inrae.fr" + } + ], + "contacts": [], + "license": "", + "tags": [], + "links": [], + "issues": [], + "dependencies": {}, + "fragment": { + "openfluid-components": ["tools", "utils", "ware"] + } +} diff --git a/src/observers/export.vars.files.kml-plot/src/CMakeLists.txt b/src/observers/export.vars.files.kml-plot/src/CMakeLists.txt index a60dacb99..9259447b2 100644 --- a/src/observers/export.vars.files.kml-plot/src/CMakeLists.txt +++ b/src/observers/export.vars.files.kml-plot/src/CMakeLists.txt @@ -23,7 +23,7 @@ SET(WARE_LINK_LIBS ${GDAL_LIBRARIES}) OPENFLUID_ADD_WAREPLUGIN( # list of C++ files - CPP_FILES KmlFilesPlotObs.cpp + CPP_FILES WareMain.cpp # list of Fortran files #FORTRAN_FILES # list of UI files (mainly for builderexts) diff --git a/src/observers/export.vars.files.kml-plot/src/KmlFilesPlotObs.cpp b/src/observers/export.vars.files.kml-plot/src/WareMain.cpp similarity index 99% rename from src/observers/export.vars.files.kml-plot/src/KmlFilesPlotObs.cpp rename to src/observers/export.vars.files.kml-plot/src/WareMain.cpp index 936bd1ebc..5ff982497 100644 --- a/src/observers/export.vars.files.kml-plot/src/KmlFilesPlotObs.cpp +++ b/src/observers/export.vars.files.kml-plot/src/WareMain.cpp @@ -31,7 +31,7 @@ /** - @file KmlFilesPlotObs.cpp + @file WareMain.cpp @author Jean-Christophe FABRE */ @@ -42,13 +42,13 @@ #include #include +#include + #include #include #include #include -#include "../../KmlObserverBase.hpp" - class KmlUnitInfoExtra : public KmlUnitInfo { diff --git a/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/CMakeLists.txt b/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/CMakeLists.txt new file mode 100644 index 000000000..140b9bfa6 --- /dev/null +++ b/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/CMakeLists.txt @@ -0,0 +1,5 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.10) + +PROJECT("observer.kml.base") + +FIND_PACKAGE(OpenFLUID COMPONENTS tools utils ware REQUIRED) diff --git a/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/KmlObserverBase.hpp b/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/KmlObserverBase.hpp new file mode 100644 index 000000000..b6d6e6c45 --- /dev/null +++ b/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/KmlObserverBase.hpp @@ -0,0 +1,377 @@ +/* + + This file is part of OpenFLUID software + Copyright(c) 2007, INRA - Montpellier SupAgro + + + == GNU General Public License Usage == + + OpenFLUID is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFLUID is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenFLUID. If not, see . + + + == Other Usage == + + Other Usage means a use of OpenFLUID that is inconsistent with the GPL + license, and requires a written agreement between You and INRA. + Licensees for Other Usage of OpenFLUID may use this file in accordance + with the terms contained in the written agreement between You and INRA. + +*/ + + +/** + @file KmlObserverBase.hpp + + @author Jean-Christophe FABRE + */ + + +#ifndef __KMLOBSERVERBASE_HPP__ +#define __KMLOBSERVERBASE_HPP__ + + +#include + +#include +#include +#include +#include +#include +#include +#include + + +// ===================================================================== +// ===================================================================== + + +class KmlUnitInfo +{ + public: + + std::string CoordsStr; + + OGRwkbGeometryType GeometryType; + + openfluid::core::UnitID_t UnitID; + + KmlUnitInfo() : CoordsStr(""), GeometryType(wkbUnknown) + {}; +}; + + +// ===================================================================== +// ===================================================================== + + +template +class KmlLayerInfo +{ + public: + + openfluid::core::UnitsClass_t UnitsClass; + + bool SourceIsDatastore; + std::string SourceID; + std::string SourceFilename; + + + std::map UnitsInfos; + + int LineWidth; + + KmlLayerInfo() : UnitsClass(""), + SourceIsDatastore(false), SourceID(""), SourceFilename (""), + LineWidth(1) + {}; + +}; + + +// ===================================================================== +// ===================================================================== + + +class KmlObserverBase : public openfluid::ware::PluggableObserver +{ + protected: + + std::string m_TmpSubDirRoot; + std::string m_KmzSubDir; + const std::string m_KmzDataSubDir; + + std::string m_Title; + std::string m_OutputFileName; + std::string m_InputDir; + std::string m_OutputDir; + std::string m_TmpDir; + + bool m_TryOpenGEarth; + + bool m_OKToGo; + + template + bool transformVectorLayerToKmlGeometry(KmlLayerInfo& LayerInfo) + { + + // TODO manage when data are coming from datastore + + GDALDataset_COMPAT* DataSource; + OGRLayer *Layer; + OGRFeature *Feature; + + + DataSource = GDALOpenRO_COMPAT(LayerInfo.SourceFilename.c_str()); + if( DataSource == nullptr ) + { + OPENFLUID_LogWarning("Cannot open shapefile "+LayerInfo.SourceFilename+". This Kml output is ignored."); + return false; + } + + std::string LayerName = openfluid::tools::FilesystemPath(LayerInfo.SourceFilename).basename(); + + Layer = DataSource->GetLayerByName(LayerName.c_str()); + + if (Layer == nullptr) + { + OPENFLUID_LogWarning("Cannot open shapefile layer from " + LayerInfo.SourceFilename + + ". This Kml output is ignored."); + return false; + } + + + int OfldIDFieldIndex = Layer->GetLayerDefn()->GetFieldIndex("OFLD_ID"); + + if (OfldIDFieldIndex < 0) + { + OPENFLUID_LogWarning("Cannot find OFLD_ID attribute in " + LayerInfo.SourceFilename + + ". This Kml output is ignored."); + return false; + } + + + Layer->ResetReading(); + while((Feature = Layer->GetNextFeature()) != nullptr) + { + openfluid::core::UnitID_t UnitID = Feature->GetFieldAsInteger(OfldIDFieldIndex); + + if (OPENFLUID_IsUnitExist(LayerInfo.UnitsClass,UnitID)) + { + + OGRGeometry *Geometry; + + Geometry = Feature->GetGeometryRef(); + + std::stringstream CoordSS; + + CoordSS << std::fixed << std::setprecision(12); + + T KUI; + + KUI.UnitID = UnitID; + + if (Geometry != nullptr) + { + + if (Geometry->getGeometryType() != wkbPolygon && Geometry->getGeometryType() != wkbLineString) + { + OPENFLUID_LogWarning("Unsupported geometry type in " + LayerInfo.SourceFilename + + ". This Kml output is ignored."); + return false; + } + + KUI.GeometryType = Geometry->getGeometryType(); + + // polygons + if (Geometry->getGeometryType() == wkbPolygon) + { + OGRLinearRing* Ring; + + Ring = ((OGRPolygon*)(Geometry))->getExteriorRing(); + + int NumPoints = Ring->getNumPoints()-1; + + for (int i=0;igetPoint(i,Point); + CoordSS << " " << Point->getX() << "," << Point->getY(); + delete Point; + } + + // close the polygon + if (NumPoints > 0) + { + OGRPoint *Point = new OGRPoint(); + Ring->getPoint(0,Point); + CoordSS << " " << Point->getX() << "," << Point->getY(); + delete Point; + } + + } + + + // line strings + if (Geometry->getGeometryType() == wkbLineString) + { + + OGRLineString* LineString; + LineString = (OGRLineString*)(Geometry); + + int NumPoints = LineString->getNumPoints(); + + for (int i=0;igetPoint(i,Point); + CoordSS << " " << Point->getX() << "," << Point->getY(); + delete Point; + } + } + + KUI.CoordsStr = CoordSS.str(); + + LayerInfo.UnitsInfos[UnitID] = KUI; + + } + else + { + OPENFLUID_LogWarning("Wrong geometry reference in " + LayerInfo.SourceFilename + + ". This Kml output is ignored."); + return false; + } + + } + OGRFeature::DestroyFeature(Feature); + } + + + GDALClose_COMPAT(DataSource); + + + return true; + } + + + // ===================================================================== + // ===================================================================== + + + void buildKmzFile() + { + std::string InputDir = m_TmpDir + "/" + m_KmzSubDir + "/"; + std::string KmzFilePath = m_OutputDir + "/" + m_OutputFileName; + + openfluid::tools::FilesystemPath(KmzFilePath).removeDirectory(); + + std::vector FoundFiles = openfluid::tools::Filesystem::findDirectories(InputDir,true); + FoundFiles << openfluid::tools::Filesystem::findFiles(InputDir,true); + + openfluid::utils::Process::Command Cmd = openfluid::utils::CMakeProxy::getTarCompressCommand(InputDir, + KmzFilePath, + FoundFiles); + + Cmd.Args << "--format=zip"; + + openfluid::utils::Process::execute(Cmd); + } + + + // ===================================================================== + // ===================================================================== + + + void tryOpenGEarth() + { + if (m_TryOpenGEarth) + { + openfluid::utils::ExternalProgram GEarthProgram = + openfluid::utils::ExternalProgram::getRegisteredProgram( + openfluid::utils::ExternalProgram::RegisteredPrograms::GoogleEarth); + + if (GEarthProgram.isFound()) + { + openfluid::utils::Process::execute( + GEarthProgram.getFullProgramPath(),{openfluid::tools::Path({m_OutputDir,m_OutputFileName}).toGeneric()}); + } + else + { + OPENFLUID_LogWarning("Cannot find Google Earth"); + } + } + } + + + // ===================================================================== + // ===================================================================== + + + void prepareTempDirectory() + { + std::string OFTmpDir; + OPENFLUID_GetRunEnvironment("dir.temp",OFTmpDir); + + m_TmpDir = openfluid::tools::Filesystem::makeUniqueSubdirectory(OFTmpDir,m_TmpSubDirRoot); + auto TmpDirFSP = openfluid::tools::FilesystemPath(m_TmpDir); + + TmpDirFSP.makeDirectory(); + + if (!TmpDirFSP.isDirectory()) + { + OPENFLUID_LogWarning("Cannot initialize temporary directory"); + m_OKToGo = false; + return; + } + + TmpDirFSP.removeDirectory(m_KmzSubDir); + TmpDirFSP.makeDirectory(m_KmzSubDir); + + if (!TmpDirFSP.isDirectory(m_KmzSubDir)) + { + OPENFLUID_LogWarning("Cannot initialize kmz temporary directory"); + m_OKToGo = false; + return; + } + + + TmpDirFSP.makeDirectory(m_KmzSubDir+"/"+m_KmzDataSubDir); + + if (!TmpDirFSP.isDirectory(m_KmzSubDir+"/"+m_KmzDataSubDir)) + { + OPENFLUID_LogWarning("Cannot initialize kmz data temporary directory"); + m_OKToGo = false; + return; + } + } + + + public: + + KmlObserverBase() : openfluid::ware::PluggableObserver(), + m_KmzSubDir("kmz"),m_KmzDataSubDir("data"), + m_Title("OpenFLUID simulation with time animation"), + m_OutputFileName(""), + m_InputDir(""), m_OutputDir(""), m_TmpDir(""), + m_TryOpenGEarth(false), + m_OKToGo(false) + { + + } + + +}; + + +#endif /* __KMLOBSERVERBASE_HPP__ */ + diff --git a/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/README.md b/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/README.md new file mode 100644 index 000000000..379518693 --- /dev/null +++ b/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/README.md @@ -0,0 +1 @@ +# observer.kml.base fragment \ No newline at end of file diff --git a/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/openfluid-fragment.json b/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/openfluid-fragment.json new file mode 100644 index 000000000..85550c457 --- /dev/null +++ b/src/observers/export.vars.files.kml-plot/src/fragments/observer.kml.base/openfluid-fragment.json @@ -0,0 +1,22 @@ +{ + "id": "observer.kml.base", + "name": "", + "description": "", + "version": "0.1", + "status": "alpha", + "authors": [ + { + "name": "Jean-Christophe Fabre", + "email": "jean-christophe.fabre@inrae.fr" + } + ], + "contacts": [], + "license": "", + "tags": [], + "links": [], + "issues": [], + "dependencies": {}, + "fragment": { + "openfluid-components": ["tools", "utils", "ware"] + } +} diff --git a/src/observers/export.vars.plot.gnuplot/src/CMakeLists.txt b/src/observers/export.vars.plot.gnuplot/src/CMakeLists.txt index 9f9beb42f..8827c020a 100644 --- a/src/observers/export.vars.plot.gnuplot/src/CMakeLists.txt +++ b/src/observers/export.vars.plot.gnuplot/src/CMakeLists.txt @@ -19,10 +19,9 @@ add_compile_definitions(QT_VERSION_MAJOR=${QT_VERSION_MAJOR}) #SET(I18N_FILES_EXTRASCANS ) - OPENFLUID_ADD_WAREPLUGIN( # list of C++ files - CPP_FILES GNUplotObs.cpp GNUplotObsTools.cpp + CPP_FILES WareMain.cpp GNUplotObsTools.cpp # list of Fortran files #FORTRAN_FILES # list of UI files (mainly for builderexts) @@ -42,9 +41,9 @@ OPENFLUID_ADD_WAREPLUGIN( # enable build of parameterization UI (ON/OFF, OFF is default) WITH_PARAMSUI ON # list of C++ files for parametrization UI - PARAMSUI_CPP_FILES GNUplotObsParamsWidget.cpp GNUplotObsTools.cpp + PARAMSUI_CPP_FILES ui/GNUplotObsParamsWidget.cpp GNUplotObsTools.cpp # list of UI files for parametrization UI - PARAMSUI_UI_FILES GNUplotObsParamsWidget.ui + PARAMSUI_UI_FILES ui/GNUplotObsParamsWidget.ui # list of RC files for parametrization UI #PARAMSUI_RC_FILES # list of languages for translation of parametrization UI diff --git a/src/observers/export.vars.plot.gnuplot/src/GNUplotObs.cpp b/src/observers/export.vars.plot.gnuplot/src/WareMain.cpp similarity index 99% rename from src/observers/export.vars.plot.gnuplot/src/GNUplotObs.cpp rename to src/observers/export.vars.plot.gnuplot/src/WareMain.cpp index 30445b17c..db5c70f22 100644 --- a/src/observers/export.vars.plot.gnuplot/src/GNUplotObs.cpp +++ b/src/observers/export.vars.plot.gnuplot/src/WareMain.cpp @@ -31,7 +31,7 @@ /** - @file GNUplotObs.cpp + @file WareMain.cpp @author Jean-Christophe FABRE */ diff --git a/src/observers/export.vars.plot.gnuplot/src/GNUplotObsParamsWidget.cpp b/src/observers/export.vars.plot.gnuplot/src/ui/GNUplotObsParamsWidget.cpp similarity index 100% rename from src/observers/export.vars.plot.gnuplot/src/GNUplotObsParamsWidget.cpp rename to src/observers/export.vars.plot.gnuplot/src/ui/GNUplotObsParamsWidget.cpp diff --git a/src/observers/export.vars.plot.gnuplot/src/GNUplotObsParamsWidget.hpp b/src/observers/export.vars.plot.gnuplot/src/ui/GNUplotObsParamsWidget.hpp similarity index 98% rename from src/observers/export.vars.plot.gnuplot/src/GNUplotObsParamsWidget.hpp rename to src/observers/export.vars.plot.gnuplot/src/ui/GNUplotObsParamsWidget.hpp index 691b06ef1..fc632aef1 100644 --- a/src/observers/export.vars.plot.gnuplot/src/GNUplotObsParamsWidget.hpp +++ b/src/observers/export.vars.plot.gnuplot/src/ui/GNUplotObsParamsWidget.hpp @@ -42,7 +42,7 @@ #include -#include "GNUplotObsTools.hpp" +#include "../GNUplotObsTools.hpp" class QListWidget; diff --git a/src/observers/export.vars.plot.gnuplot/src/GNUplotObsParamsWidget.ui b/src/observers/export.vars.plot.gnuplot/src/ui/GNUplotObsParamsWidget.ui similarity index 100% rename from src/observers/export.vars.plot.gnuplot/src/GNUplotObsParamsWidget.ui rename to src/observers/export.vars.plot.gnuplot/src/ui/GNUplotObsParamsWidget.ui diff --git a/src/openfluid/base/Environment.cpp b/src/openfluid/base/Environment.cpp index 3aeea6ebc..d3e4f4908 100644 --- a/src/openfluid/base/Environment.cpp +++ b/src/openfluid/base/Environment.cpp @@ -79,6 +79,7 @@ std::vector Environment::m_DefaultBuilderextsDirs; std::vector Environment::m_ExtraBuilderextsDirs; std::string Environment::m_ProvidedExamplesDir; std::string Environment::m_UserExamplesDir; +std::string Environment::m_UserExampleObserversDir; std::string Environment::m_UserExampleSimulatorsDir; std::string Environment::m_TranslationsDir; std::string Environment::m_CommonResourcesDir; @@ -202,6 +203,9 @@ void Environment::init() m_UserExampleSimulatorsDir = openfluid::tools::Filesystem::joinPath({m_UserExamplesDir, openfluid::config::WARESDEV_PATH, openfluid::config::SIMULATORS_PATH}); + m_UserExampleObserversDir = openfluid::tools::Filesystem::joinPath({m_UserExamplesDir, + openfluid::config::WARESDEV_PATH, + openfluid::config::OBSERVERS_PATH}); m_ProvidedExamplesDir = openfluid::tools::Filesystem::joinPath({m_InstallPrefix, openfluid::config::EXAMPLES_STD_PATH}); diff --git a/src/openfluid/base/Environment.hpp b/src/openfluid/base/Environment.hpp index 01f6ceb0d..877f51530 100644 --- a/src/openfluid/base/Environment.hpp +++ b/src/openfluid/base/Environment.hpp @@ -100,6 +100,8 @@ class OPENFLUID_API Environment static std::string m_UserExamplesDir; + static std::string m_UserExampleObserversDir; + static std::string m_UserExampleSimulatorsDir; static std::string m_TranslationsDir; @@ -260,6 +262,15 @@ class OPENFLUID_API Environment { return m_UserExampleSimulatorsDir; } + + /** + Returns the full path of the example observers directory + @return the full path to the directory + */ + static std::string getUserExampleObserversDir() + { + return m_UserExampleObserversDir; + } /** Returns the full path of the directory where examples provided by OpenFLUID or models installations are stored diff --git a/src/openfluid/base/ExamplesManager.cpp b/src/openfluid/base/ExamplesManager.cpp index b9aadafcd..0f95c39d9 100644 --- a/src/openfluid/base/ExamplesManager.cpp +++ b/src/openfluid/base/ExamplesManager.cpp @@ -141,6 +141,30 @@ bool ExamplesManager::installSimulator(const std::string& SimulatorDir, // ===================================================================== +bool ExamplesManager::installObserver(const std::string& ObserverDir, + const std::string& ResourcesPath, const std::string& InstallPath, + const bool Force) +{ + std::string FromPath = openfluid::tools::Filesystem::joinPath({buildRessourcesPath(ResourcesPath), + openfluid::config::WARESDEV_PATH, + openfluid::config::OBSERVERS_PATH}); + if (!openfluid::tools::FilesystemPath({FromPath,ObserverDir}).isDirectory()) + { + // silent since called for every ware import + return false; + } + std::string ToPath = openfluid::tools::Filesystem::joinPath({buildInstallPath(InstallPath), + openfluid::config::WARESDEV_PATH, + openfluid::config::OBSERVERS_PATH}); + std::cout << "-- Installing observer " << ObserverDir << " from " << FromPath << " to " << ToPath << std::endl; + return installDirectory(FromPath,ToPath,ObserverDir,Force); +} + + +// ===================================================================== +// ===================================================================== + + bool ExamplesManager::installAllProjects(const std::string& ResourcesPath, const std::string& InstallPath, const bool Force) { @@ -156,7 +180,7 @@ bool ExamplesManager::installAllProjects(const std::string& ResourcesPath, const bool AllIsOK = true; for (const auto& Prj : FoundProjects) { - AllIsOK += installProject(Prj,ResPath,InstPath,Force); + AllIsOK &= installProject(Prj,ResPath,InstPath,Force); } return AllIsOK; } @@ -179,19 +203,30 @@ bool ExamplesManager::installAllSimulators(const std::string& ResourcesPath, con openfluid::config::WARESDEV_PATH, openfluid::config::SIMULATORS_PATH}); + bool AllIsOK = true; if (openfluid::tools::FilesystemPath(SimulatorsPath).isDirectory()) { std::vector FoundSimulators = openfluid::tools::Filesystem::findDirectories(SimulatorsPath); - bool AllIsOK = true; for (const auto& Sim : FoundSimulators) { - AllIsOK += installSimulator(Sim,ResPath,InstPath,Force); + AllIsOK &= installSimulator(Sim,ResPath,InstPath,Force); } - return AllIsOK; } + + std::string ObserversPath = openfluid::tools::Filesystem::joinPath({ResPath, + openfluid::config::WARESDEV_PATH, + openfluid::config::OBSERVERS_PATH}); + if (openfluid::tools::FilesystemPath(ObserversPath).isDirectory()) + { + std::vector FoundObservers = openfluid::tools::Filesystem::findDirectories(ObserversPath); - return false; + for (const auto& Obs : FoundObservers) + { + AllIsOK &= installObserver(Obs,ResPath,InstPath,Force); + } + } + return AllIsOK; } diff --git a/src/openfluid/base/ExamplesManager.hpp b/src/openfluid/base/ExamplesManager.hpp index 18e3a075f..00080fb48 100644 --- a/src/openfluid/base/ExamplesManager.hpp +++ b/src/openfluid/base/ExamplesManager.hpp @@ -93,6 +93,10 @@ class OPENFLUID_API ExamplesManager const std::string& ResourcesPath = "", const std::string& InstallPath = "", const bool Force = false); + static bool installObserver(const std::string& ObserverDir, + const std::string& ResourcesPath = "", const std::string& InstallPath = "", + const bool Force = false); + /** Installs all example projects @param[in] ResourcesPath Path to the examples ressources, diff --git a/src/openfluid/machine/GeneratorSignature.cpp b/src/openfluid/machine/GeneratorSignature.cpp index 8732811bb..d9815ce64 100644 --- a/src/openfluid/machine/GeneratorSignature.cpp +++ b/src/openfluid/machine/GeneratorSignature.cpp @@ -88,7 +88,7 @@ GeneratorSignature::GeneratorSignature(openfluid::fluidx::GeneratorDescriptor::G { TypedVarName += "["+VarDimType+"]"; } - SimulatorHandledData.ProducedVars.push_back(openfluid::ware::SignatureSpatialDataItem(TypedVarName, + SimulatorHandledData.ProducedVars.push_back(openfluid::ware::SignatureVariableItem(TypedVarName, VarPair.UnitsClass,"","")); } } diff --git a/src/openfluid/machine/InjectGenerator.hpp b/src/openfluid/machine/InjectGenerator.hpp index fb6bb01ba..145ae916d 100644 --- a/src/openfluid/machine/InjectGenerator.hpp +++ b/src/openfluid/machine/InjectGenerator.hpp @@ -54,7 +54,7 @@ namespace openfluid { namespace machine { class OPENFLUID_API InjectGenerator : public MonoGenerator { - private: + protected: bool m_IsMin; bool m_IsMax; diff --git a/src/openfluid/machine/tests/Engine_TEST.cpp b/src/openfluid/machine/tests/Engine_TEST.cpp index abf925713..20e330f5c 100644 --- a/src/openfluid/machine/tests/Engine_TEST.cpp +++ b/src/openfluid/machine/tests/Engine_TEST.cpp @@ -45,14 +45,15 @@ #include -#include +#include #include #include #include #include #include #include -#include +#include +#include #include "tests-config.hpp" @@ -118,11 +119,11 @@ BOOST_AUTO_TEST_CASE(check_construction) openfluid::machine::Engine Eng(SBlob,Model,Monitoring,MachineListen); - BOOST_CHECK_THROW(openfluid::ware::SignatureSpatialDataItem("var1[toto]","UA","",""), + BOOST_CHECK_THROW(openfluid::ware::SignatureVariableItem("var1[toto]","UA","",""), openfluid::base::FrameworkException); - BOOST_CHECK_THROW(openfluid::ware::SignatureSpatialDataItem("var1(double)","UA","",""), + BOOST_CHECK_THROW(openfluid::ware::SignatureVariableItem("var1(double)","UA","",""), openfluid::base::FrameworkException); - BOOST_CHECK_THROW(openfluid::ware::SignatureSpatialDataItem("var1[double","UA","",""), + BOOST_CHECK_THROW(openfluid::ware::SignatureVariableItem("var1[double","UA","",""), openfluid::base::FrameworkException); delete MachineListen; @@ -159,8 +160,8 @@ BOOST_AUTO_TEST_CASE(check_pretests) openfluid::machine::WareContainer Cont1(openfluid::ware::WareType::SIMULATOR); auto Sign1 = new openfluid::ware::SimulatorSignature(); Sign1->ID = "MySim1"; - Sign1->HandledData.RequiredVars.push_back(openfluid::ware::SignatureSpatialDataItem("var1","UA","","")); - Sign1->SimulatorHandledData.UpdatedVars.push_back(openfluid::ware::SignatureSpatialDataItem("var5[]","UB","","")); + Sign1->HandledData.RequiredVars.push_back(openfluid::ware::SignatureVariableItem("var1","UA","","")); + Sign1->SimulatorHandledData.UpdatedVars.push_back(openfluid::ware::SignatureVariableItem("var5[]","UB","","")); Cont1.setSignature(Sign1); Cont1.validate(); @@ -179,7 +180,7 @@ BOOST_AUTO_TEST_CASE(check_pretests) openfluid::machine::WareContainer Cont2(openfluid::ware::WareType::SIMULATOR); auto Sign2 = new openfluid::ware::SimulatorSignature(); Sign2->ID = "MySim0"; - Sign2->SimulatorHandledData.ProducedVars.push_back(openfluid::ware::SignatureSpatialDataItem("var1","UA","","")); + Sign2->SimulatorHandledData.ProducedVars.push_back(openfluid::ware::SignatureVariableItem("var1","UA","","")); Cont2.setSignature(Sign2); Cont2.validate(); @@ -198,7 +199,7 @@ BOOST_AUTO_TEST_CASE(check_pretests) openfluid::machine::WareContainer Cont3(openfluid::ware::WareType::SIMULATOR); auto Sign3 = new openfluid::ware::SimulatorSignature(); Sign3->ID = "MySim2"; - Sign3->HandledData.RequiredVars.push_back(openfluid::ware::SignatureSpatialDataItem("var5[]","UB","","")); + Sign3->HandledData.RequiredVars.push_back(openfluid::ware::SignatureVariableItem("var5[]","UB","","")); Cont3.setSignature(Sign3); Cont3.validate(); @@ -217,7 +218,7 @@ BOOST_AUTO_TEST_CASE(check_pretests) openfluid::machine::WareContainer Cont4(openfluid::ware::WareType::SIMULATOR); auto Sign4 = new openfluid::ware::SimulatorSignature(); Sign4->ID = "MySim0.5"; - Sign4->HandledData.RequiredVars.push_back(openfluid::ware::SignatureSpatialDataItem("var1","UA","","")); + Sign4->HandledData.RequiredVars.push_back(openfluid::ware::SignatureVariableItem("var1","UA","","")); Cont4.setSignature(Sign4); Cont4.validate(); @@ -254,8 +255,8 @@ BOOST_AUTO_TEST_CASE(check_pretests) openfluid::machine::WareContainer Cont6(openfluid::ware::WareType::SIMULATOR); auto Sign6 = new openfluid::ware::SimulatorSignature(); Sign6->ID = "MySim4"; - Sign6->HandledData.RequiredVars.push_back(openfluid::ware::SignatureSpatialDataItem("var1","UA","","")); - Sign6->SimulatorHandledData.ProducedVars.push_back(openfluid::ware::SignatureSpatialDataItem("var7","UC","","")); + Sign6->HandledData.RequiredVars.push_back(openfluid::ware::SignatureVariableItem("var1","UA","","")); + Sign6->SimulatorHandledData.ProducedVars.push_back(openfluid::ware::SignatureVariableItem("var7","UC","","")); Cont6.setSignature(Sign6); Cont6.validate(); @@ -306,9 +307,9 @@ BOOST_AUTO_TEST_CASE(check_typed_pretests) auto Sign1 = new openfluid::ware::SimulatorSignature(); Sign1->ID = "MySim1"; Sign1->HandledData.RequiredVars.push_back( - openfluid::ware::SignatureSpatialDataItem("var1[double]","UA","","")); + openfluid::ware::SignatureVariableItem("var1[double]","UA","","")); Sign1->SimulatorHandledData.UpdatedVars.push_back( - openfluid::ware::SignatureSpatialDataItem("var5[vector]","UB","","")); + openfluid::ware::SignatureVariableItem("var5[vector]","UB","","")); Cont1.setSignature(Sign1); Cont1.validate(); @@ -328,7 +329,7 @@ BOOST_AUTO_TEST_CASE(check_typed_pretests) auto Sign2 = new openfluid::ware::SimulatorSignature(); Sign2->ID = "MySim0"; Sign2->SimulatorHandledData.ProducedVars.push_back( - openfluid::ware::SignatureSpatialDataItem("var1[double]","UA","","")); + openfluid::ware::SignatureVariableItem("var1[double]","UA","","")); Cont2.setSignature(Sign2); Cont2.validate(); @@ -348,7 +349,7 @@ BOOST_AUTO_TEST_CASE(check_typed_pretests) auto Sign3 = new openfluid::ware::SimulatorSignature(); Sign3->ID = "MySim2"; Sign3->HandledData.RequiredVars.push_back( - openfluid::ware::SignatureSpatialDataItem("var5[vector]","UB","","")); + openfluid::ware::SignatureVariableItem("var5[vector]","UB","","")); Cont3.setSignature(Sign3); Cont3.validate(); @@ -367,7 +368,7 @@ BOOST_AUTO_TEST_CASE(check_typed_pretests) openfluid::machine::WareContainer Cont4(openfluid::ware::WareType::SIMULATOR); auto Sign4 = new openfluid::ware::SimulatorSignature(); Sign4->ID = "MySim0.5"; - Sign4->HandledData.RequiredVars.push_back(openfluid::ware::SignatureSpatialDataItem("var1","UA","","")); + Sign4->HandledData.RequiredVars.push_back(openfluid::ware::SignatureVariableItem("var1","UA","","")); Cont4.setSignature(Sign4); Cont4.validate(); @@ -404,8 +405,8 @@ BOOST_AUTO_TEST_CASE(check_typed_pretests) openfluid::machine::WareContainer Cont6(openfluid::ware::WareType::SIMULATOR); auto Sign6 = new openfluid::ware::SimulatorSignature(); Sign6->ID = "MySim0.5"; - Sign6->HandledData.RequiredVars.push_back(openfluid::ware::SignatureSpatialDataItem("var1","UA","","")); - Sign6->SimulatorHandledData.ProducedVars.push_back(openfluid::ware::SignatureSpatialDataItem("var7","UC","","")); + Sign6->HandledData.RequiredVars.push_back(openfluid::ware::SignatureVariableItem("var1","UA","","")); + Sign6->SimulatorHandledData.ProducedVars.push_back(openfluid::ware::SignatureVariableItem("var7","UC","","")); Cont6.setSignature(Sign6); Cont6.validate(); diff --git a/src/openfluid/tools/IDHelpers.hpp b/src/openfluid/tools/IDHelpers.hpp index 250259359..21cf85236 100644 --- a/src/openfluid/tools/IDHelpers.hpp +++ b/src/openfluid/tools/IDHelpers.hpp @@ -47,7 +47,7 @@ #include #include -#include +#include #include #include diff --git a/src/openfluid/tools/StringHelpers.cpp b/src/openfluid/tools/StringHelpers.cpp index a0af6b730..698047f75 100644 --- a/src/openfluid/tools/StringHelpers.cpp +++ b/src/openfluid/tools/StringHelpers.cpp @@ -39,10 +39,10 @@ #include #include - #include #include +#include #include @@ -136,6 +136,37 @@ bool contains(const std::string& Str,const std::string& SubStr, bool CaseSensiti // ===================================================================== +std::map> searchInFolder(std::string Folder, std::string String) +{ + std::map> OccurencesPosByFile; + for (const auto& E : std::filesystem::recursive_directory_iterator{Folder}) + { + const openfluid::tools::Path FileObj = openfluid::tools::Path::fromStdPath(E.path()); + if (FileObj.isFile()) + { + const std::string FileContent = openfluid::tools::Filesystem::readFile(FileObj); + auto It = FileContent.find(String); + + while (It != std::string::npos) + { + const std::string PathString = E.path().string(); + if (OccurencesPosByFile.find(PathString) == OccurencesPosByFile.end()) + { + OccurencesPosByFile[PathString] = {}; + } + OccurencesPosByFile[PathString].insert(It); + It = FileContent.find(String, It+String.size()); + } + } + } + return OccurencesPosByFile; +} + + +// ===================================================================== +// ===================================================================== + + std::string replace(const std::string& Str,const std::string& SearchStr, const std::string& ReplaceStr) { return boost::replace_all_copy(Str,SearchStr,ReplaceStr); @@ -146,6 +177,20 @@ std::string replace(const std::string& Str,const std::string& SearchStr, const s // ===================================================================== +std::string replace_once(const std::string& Str,const std::string& SearchStr, const std::string& ReplaceStr, + std::size_t Pos) +{ + std::string Prefix = Str.substr(0,Pos); + std::string SubStr = Str.substr(Pos, Str.size()); + std::string Res = boost::replace_first_copy(SubStr,SearchStr,ReplaceStr); + return Prefix+Res; +} + + +// ===================================================================== +// ===================================================================== + + std::vector split(const std::string& Str, const char Sep, bool KeepEmpty) { return split(Str,std::string(1,Sep),KeepEmpty); diff --git a/src/openfluid/tools/StringHelpers.hpp b/src/openfluid/tools/StringHelpers.hpp index 754c7e62c..5c4eef485 100644 --- a/src/openfluid/tools/StringHelpers.hpp +++ b/src/openfluid/tools/StringHelpers.hpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -181,6 +182,15 @@ bool OPENFLUID_API endsWith(const std::string& Str,const std::string& SubStr); bool OPENFLUID_API contains(const std::string& Str,const std::string& SubStr, bool CaseSensitive = true); +/** + Find all occurences of a substring in a folder, iterating on any subdirectory + @param[in] Folder the location to check + @param[in] String the string to look for + @return a map containing the file path associated with a set of string locations in this file +*/ +std::map> OPENFLUID_API searchInFolder(std::string Folder, std::string String); + + /** Replaces every occurrence in a string of a searched substring by a replacement substring @snippet misc/strings.cpp str_repl @@ -191,6 +201,9 @@ bool OPENFLUID_API contains(const std::string& Str,const std::string& SubStr, bo */ std::string OPENFLUID_API replace(const std::string& Str,const std::string& SearchStr, const std::string& ReplaceStr); +std::string OPENFLUID_API replace_once(const std::string& Str,const std::string& SearchStr, + const std::string& ReplaceStr, + std::size_t Pos); /** Formats a string using a format and a variable number of arguments. diff --git a/src/openfluid/ui/waresdev/WareSrcExplorer.cpp b/src/openfluid/ui/waresdev/WareSrcExplorer.cpp index 302deb045..f72fd447f 100644 --- a/src/openfluid/ui/waresdev/WareSrcExplorer.cpp +++ b/src/openfluid/ui/waresdev/WareSrcExplorer.cpp @@ -155,7 +155,9 @@ void WareSrcExplorer::onCustomContextMenuRequested(const QPoint& Point) if (getCurrentPath().toStdString() == openfluid::waresdev::WareSrcEnquirer::getWareInfoFromPath(getCurrentPath().toStdString()).AbsoluteWarePath) { + Menu.addAction(tr("Duplicate ware"), this, SIGNAL(duplicateWareAsked())); Menu.addAction(tr("Delete ware"), this, SIGNAL(deleteWareAsked())); + } else if (!IsRemoveFragment) // "delete folder" is hidden when "remove this fragment" already there { diff --git a/src/openfluid/ui/waresdev/WareSrcExplorer.hpp b/src/openfluid/ui/waresdev/WareSrcExplorer.hpp index 06b2afddb..d97711597 100644 --- a/src/openfluid/ui/waresdev/WareSrcExplorer.hpp +++ b/src/openfluid/ui/waresdev/WareSrcExplorer.hpp @@ -130,6 +130,8 @@ class OPENFLUID_API WareSrcExplorer: public QTreeView void openPathAsked(const QString& FilePath); + void duplicateWareAsked(); + void deleteWareAsked(); void fileDeleted(const QString& Path); diff --git a/src/openfluid/ui/waresdev/WareSrcFileEditor.cpp b/src/openfluid/ui/waresdev/WareSrcFileEditor.cpp index 77f948731..8427b699a 100644 --- a/src/openfluid/ui/waresdev/WareSrcFileEditor.cpp +++ b/src/openfluid/ui/waresdev/WareSrcFileEditor.cpp @@ -330,9 +330,38 @@ void WareSrcFileEditor::keyPressEvent(QKeyEvent* Event) { insertNewLine(); } + else if ((Event->modifiers() & Qt::ShiftModifier) && Key == Qt::Key_Backtab) + { + // Unindentation + QTextCursor TextCursor = textCursor(); + if(TextCursor.hasSelection()) + { + removeMultilinePrefix(m_IndentString); + } + else + { + removeLinePrefix(m_IndentString, TextCursor.block()); + } + } else if (Key == Qt::Key_Tab) { - insertPlainText(m_IndentString); + // Indentation + QTextCursor TextCursor = textCursor(); + int StartLine = document()->findBlock(TextCursor.selectionStart()).firstLineNumber(); + int EndLine = document()->findBlock(TextCursor.selectionEnd()).firstLineNumber(); + if(TextCursor.hasSelection() && (StartLine != EndLine)) + { + addMultilinePrefix(m_IndentString); + } + else + { + insertPlainText(m_IndentString); + } + } + else if ((Event->modifiers() & Qt::ControlModifier) && Key == Qt::Key_Slash) + { + // Comment / Uncomment + handleSelectionCommenting(); } else if ((Event->modifiers() & Qt::ControlModifier) && Key == Qt::Key_I) { @@ -406,6 +435,288 @@ void WareSrcFileEditor::keyPressEvent(QKeyEvent* Event) // ===================================================================== +void WareSrcFileEditor::handleSelectionCommenting() +{ + QString CommentStr = openfluid::ui::waresdev::WareSrcFiletypeManager::instance() + ->getFileCommentString(m_FilePath); + if(!CommentStr.isEmpty()) + { + QTextCursor TextCursor = textCursor(); + QTextBlock Block = TextCursor.block(); + int EndPos = TextCursor.selectionEnd(); + bool NeedComments = false; + QString PrettyCommentStr = CommentStr + CommentExtraSpaceString; + if(TextCursor.hasSelection()) + { + while (Block.isValid() && (Block.position() < EndPos)) + { + if(!Block.text().isEmpty() && !Block.text().trimmed().startsWith(CommentStr)) + { + NeedComments = true; + break; + } + Block = Block.next(); + } + if(NeedComments) + { + addMultilinePrefix(PrettyCommentStr); + } + else + { + removeMultilinePrefix(CommentStr); + } + } + else + { + if(!Block.text().isEmpty() && Block.text().trimmed().startsWith(CommentStr)) + { + removeLinePrefix(CommentStr, Block); + } + else + { + addLinePrefix(PrettyCommentStr, Block); + } + } + } +} + + +// ===================================================================== +// ===================================================================== + + +bool WareSrcFileEditor::hasCharInBlockBeforePos(int Pos) +{ + QTextCursor Cursor = textCursor(); + Cursor.setPosition(Pos); + QTextBlock Block = Cursor.block(); + if (Pos > Block.position()) + { + Cursor.setPosition(Pos, QTextCursor::MoveAnchor); + Cursor.setPosition(Block.position(), QTextCursor::KeepAnchor); + return getLeadingSpacesCount(Cursor.selectedText()) != Cursor.selectedText().length(); + } + + return false; +} + + +// ===================================================================== +// ===================================================================== + + +int WareSrcFileEditor::getLeadingSpacesCount(const QString& QStr) +{ + int Count = 0; + for (QChar Char : QStr) + { + if (!Char.isSpace()) + { + return Count; + } + Count++; + } + + return Count; +} + + +// ===================================================================== +// ===================================================================== + + +WareSrcFileEditor::SelectionData WareSrcFileEditor::createSelectionData() +{ + SelectionData Data; + Data.TextCursor = textCursor(); + Data.CursorPos = Data.TextCursor.position(); + Data.StartPos = Data.TextCursor.selectionStart(); + Data.EndPos = Data.TextCursor.selectionEnd(); + Data.FirstBlock = document()->findBlock(Data.StartPos); + + return Data; +} + + +// ===================================================================== +// ===================================================================== + + +void WareSrcFileEditor::selectText(int StartPos, int EndPos) +{ + QTextCursor TextCursor = textCursor(); + TextCursor.setPosition(StartPos); + TextCursor.setPosition(EndPos, QTextCursor::KeepAnchor); + setTextCursor(TextCursor); +} + + +// ===================================================================== +// ===================================================================== + + +void WareSrcFileEditor::addLinePrefix(const QString& PrefixStr, const QTextBlock& Block) +{ + QTextCursor TextCursor = textCursor(); + int CursorPos = TextCursor.position(); + /* Indicates the start of a block of editing operations on the document + that should appear as a single operation from an undo/redo point of view. */ + TextCursor.beginEditBlock(); + + int LeadingSpacesCount = getLeadingSpacesCount(Block.text()); + QString UpdatedText = Block.text().insert(LeadingSpacesCount, PrefixStr); + selectText(Block.position(), Block.position() + Block.length()-1); + insertPlainText(UpdatedText); + TextCursor.setPosition(CursorPos + (hasCharInBlockBeforePos(CursorPos) ? PrefixStr.length() : 0)); + setTextCursor(TextCursor); + + TextCursor.endEditBlock(); +} + + +// ===================================================================== +// ===================================================================== + + +int WareSrcFileEditor::removeLinePrefix(const QString& PrefixStr, const QTextBlock& Block) +{ + QTextCursor TextCursor = textCursor(); + int LeadingSpacesCount = getLeadingSpacesCount(Block.text()); + int RemoveSize = 0; + bool ShouldRemove = false; + QString UpdatedText; + int CursorPos = TextCursor.position(); + TextCursor.beginEditBlock(); + + // Specific use case for unindentation + if(PrefixStr == m_IndentString && LeadingSpacesCount > 0) + { + RemoveSize = (LeadingSpacesCount > m_IndentString.length()) ? m_IndentString.length() : LeadingSpacesCount; + UpdatedText = Block.text().remove(0, RemoveSize); + ShouldRemove = true; + } + else if(Block.text().trimmed().startsWith(PrefixStr)) + { + int ExtraSpacePos = Block.position() + LeadingSpacesCount + PrefixStr.length(); + TextCursor.setPosition(ExtraSpacePos); + TextCursor.setPosition(ExtraSpacePos + CommentExtraSpaceString.length(), QTextCursor::KeepAnchor); + setTextCursor(TextCursor); + // Remove extra first space after prefix + int ExtraSize = (!TextCursor.selectedText().isEmpty() && TextCursor.selectedText().trimmed().isEmpty()) ? + CommentExtraSpaceString.length() : 0; + UpdatedText = Block.text().remove(LeadingSpacesCount, PrefixStr.length() + ExtraSize); + RemoveSize = PrefixStr.length() + ExtraSize; + ShouldRemove = true; + } + + if(ShouldRemove) + { + bool ShouldStepBackCursor = hasCharInBlockBeforePos(CursorPos) || PrefixStr == m_IndentString; + selectText(Block.position(), Block.position() + Block.length()-1); + insertPlainText(UpdatedText); + TextCursor.setPosition(std::max(CursorPos - (ShouldStepBackCursor ? RemoveSize : 0), Block.position())); + setTextCursor(TextCursor); + } + TextCursor.endEditBlock(); + + return RemoveSize; +} + + +// ===================================================================== +// ===================================================================== + + +void WareSrcFileEditor::addMultilinePrefix(const QString& PrefixStr) +{ + SelectionData Data = createSelectionData(); + Data.TextCursor.beginEditBlock(); + + Data.TextCursor.clearSelection(); + QTextBlock CurrentBlock = Data.FirstBlock; + bool SelectionInWord = hasCharInBlockBeforePos(Data.StartPos); + int TotalUpdateSize = 0; + + // Iterate through lines to indent + while (CurrentBlock.isValid() && (CurrentBlock.position() < (Data.EndPos + TotalUpdateSize))) + { + if(!CurrentBlock.text().isEmpty()) + { + addLinePrefix(PrefixStr, CurrentBlock); + TotalUpdateSize += PrefixStr.length(); + } + CurrentBlock = CurrentBlock.next(); + } + + // Restore selection + // Maintain anchor on indentation if the cursor is in leading spaces of first block + int StartPosUpdated = Data.StartPos + (SelectionInWord ? PrefixStr.size() : 0); + int EndPosUpdated = Data.EndPos + TotalUpdateSize; + //selectText(StartPosUpdated, EndPosUpdated, Data.CursorPos == Data.StartPos); + Data.CursorPos == Data.StartPos ? selectText(EndPosUpdated, StartPosUpdated) : + selectText(StartPosUpdated, EndPosUpdated); + + Data.TextCursor.endEditBlock(); +} + + +// ===================================================================== +// ===================================================================== + + +void WareSrcFileEditor::removeMultilinePrefix(const QString& PrefixStr) +{ + SelectionData Data = createSelectionData(); + Data.TextCursor.beginEditBlock(); + + Data.TextCursor.clearSelection(); + QTextBlock CurrentBlock = Data.FirstBlock; + bool IsFirstBlock = true; + int RemoveSizeFirstBlock = 0; + int TotalUpdateSize = 0; + + // Iterate through lines to indent + while (CurrentBlock.isValid() && (CurrentBlock.position() < (Data.EndPos - TotalUpdateSize))) + { + if(!CurrentBlock.text().isEmpty()) + { + int RemoveSize = removeLinePrefix(PrefixStr, CurrentBlock); + TotalUpdateSize += RemoveSize; + if(IsFirstBlock) + { + // Accurate removing size of first block + RemoveSizeFirstBlock = RemoveSize; + IsFirstBlock = false; + } + } + CurrentBlock = CurrentBlock.next(); + } + + // Restore selection + int TempStartPosUpdated; + if(TotalUpdateSize == 0 || !hasCharInBlockBeforePos(Data.StartPos)) + { + TempStartPosUpdated = Data.StartPos; + } + else + { + TempStartPosUpdated = Data.StartPos - RemoveSizeFirstBlock; + } + int StartPosUpdated = std::clamp(TempStartPosUpdated, Data.FirstBlock.position(), Data.EndPos); + + int EndPosUpdated = Data.EndPos - TotalUpdateSize; + //selectText(StartPosUpdated, EndPosUpdated, Data.CursorPos == Data.StartPos); + Data.CursorPos == Data.StartPos ? selectText(EndPosUpdated, StartPosUpdated) : + selectText(StartPosUpdated, EndPosUpdated); + + Data.TextCursor.endEditBlock(); +} + + +// ===================================================================== +// ===================================================================== + + void WareSrcFileEditor::insertCompletion() { QTextCursor Cursor = textCursor(); @@ -497,6 +808,11 @@ void WareSrcFileEditor::resizeEvent(QResizeEvent* Event) void WareSrcFileEditor::contextMenuEvent(QContextMenuEvent* Event) { QMenu* Menu = createStandardContextMenu(); + + QAction *CommentingAction = new QAction("Comment/Uncomment", this); + CommentingAction->setShortcut(QKeySequence(tr("Ctrl+/"))); + Menu->addAction(CommentingAction); + connect(CommentingAction, &QAction::triggered, this, &WareSrcFileEditor::handleSelectionCommenting); Menu->addSeparator(); diff --git a/src/openfluid/ui/waresdev/WareSrcFileEditor.hpp b/src/openfluid/ui/waresdev/WareSrcFileEditor.hpp index 9a71a8c26..53acffefa 100644 --- a/src/openfluid/ui/waresdev/WareSrcFileEditor.hpp +++ b/src/openfluid/ui/waresdev/WareSrcFileEditor.hpp @@ -41,9 +41,11 @@ #include +#include #include #include #include +#include #include #include @@ -191,6 +193,21 @@ class OPENFLUID_API WareSrcFileEditor: public QPlainTextEdit, public WareFileEdi bool m_ShowLineMarkers = true; + struct SelectionData + { + QTextCursor TextCursor; + + QTextBlock FirstBlock; + + int CursorPos; + + int StartPos; + + int EndPos; + }; + + QString CommentExtraSpaceString = " "; + void writeString(const QString& Str, int InitialIndentInSpaceNb); void insertNewLine(); @@ -199,6 +216,31 @@ class OPENFLUID_API WareSrcFileEditor: public QPlainTextEdit, public WareFileEdi bool replaceString(const QString& StringToFind, const QString& StringForReplace, Qt::CaseSensitivity Cs); + int getLeadingSpacesCount(const QString& QStr); + + bool hasCharInBlockBeforePos(int Pos); + + void selectText(int StartPos, int EndPos); + + SelectionData createSelectionData(); + + void addLinePrefix(const QString& PrefixStr, const QTextBlock& Block); + + /** + Remove a prefix string from a block text. (It also removes one extra space (if found) after prefix string) + If the block text does not start with the prefix string (excluding spaces), it does nothing. + @param[in] PrefixStr The prefix string to remove from block text + @param[in] Block The block where to remove the prefix string + @return the number of characters removed from the block text + */ + int removeLinePrefix(const QString& PrefixStr, const QTextBlock& Block); + + void addMultilinePrefix(const QString& PrefixStr); + + void removeMultilinePrefix(const QString& PrefixStr); + + void handleSelectionCommenting(); + protected: diff --git a/src/openfluid/ui/waresdev/WareSrcFiletypeManager.cpp b/src/openfluid/ui/waresdev/WareSrcFiletypeManager.cpp index 0f7dcb868..f5076201c 100644 --- a/src/openfluid/ui/waresdev/WareSrcFiletypeManager.cpp +++ b/src/openfluid/ui/waresdev/WareSrcFiletypeManager.cpp @@ -287,6 +287,7 @@ WareSrcFiletypeManager::HighlightingRules_t WareSrcFiletypeManager::parseSyntaxF else if (TagName == "rule") { QString StyleName = QString::fromStdString(openfluid::thirdparty::getXMLAttribute(Elt,"style")); + QString RuleName = QString::fromStdString(openfluid::thirdparty::getXMLAttribute(Elt,"name")); if (m_Formats.contains(StyleName)) { @@ -304,6 +305,17 @@ WareSrcFiletypeManager::HighlightingRules_t WareSrcFiletypeManager::parseSyntaxF if (!SimplePatternValue.isEmpty()) { + if(RuleName == "comment-singleline") + { + QRegularExpression CommentStringRegex(R"(^[^.]*)"); + const auto& Match = CommentStringRegex.match(SimplePatternValue); + if (Match.hasMatch()) + { + m_CommentStringByLangCode.insert(QString::fromStdString( + openfluid::thirdparty::getXMLAttribute(LangElt,"name")), Match.captured(0)); + } + } + #if (QT_VERSION_MAJOR < 6) Rules.append(HighlightingRule(StyleName, QRegExp(SimplePatternValue), Format)); #else @@ -400,6 +412,25 @@ QString WareSrcFiletypeManager::getFileLanguage(const QString& FilePath) const // ===================================================================== +QString WareSrcFiletypeManager::getFileCommentString(const QString& FilePath) const +{ + const auto It = m_CommentStringByLangCode.find(getFileLanguage(FilePath)); + + if (It != m_CommentStringByLangCode.end()) + { + return It.value(); + } + else + { + return QString(); + } +} + + +// ===================================================================== +// ===================================================================== + + QMap WareSrcFiletypeManager::getIconsByFileExtensionList() const { return m_IconsByFileExtensionList; diff --git a/src/openfluid/ui/waresdev/WareSrcFiletypeManager.hpp b/src/openfluid/ui/waresdev/WareSrcFiletypeManager.hpp index 2de48a026..154b63797 100644 --- a/src/openfluid/ui/waresdev/WareSrcFiletypeManager.hpp +++ b/src/openfluid/ui/waresdev/WareSrcFiletypeManager.hpp @@ -47,9 +47,8 @@ #include #if (QT_VERSION_MAJOR < 6) #include -#else -#include #endif +#include #include #include @@ -134,6 +133,8 @@ class OPENFLUID_API WareSrcFiletypeManager QMap m_WareSrcFiletypes; + QMap m_CommentStringByLangCode; + WareSrcFiletypeManager(); ~WareSrcFiletypeManager(); @@ -158,6 +159,8 @@ class OPENFLUID_API WareSrcFiletypeManager QString getFileLanguage(const QString& FilePath) const; + QString getFileCommentString(const QString& FilePath) const; + }; diff --git a/src/openfluid/ui/waresdev/WareSrcWidgetCollection.cpp b/src/openfluid/ui/waresdev/WareSrcWidgetCollection.cpp index 72a23b899..3ac425308 100644 --- a/src/openfluid/ui/waresdev/WareSrcWidgetCollection.cpp +++ b/src/openfluid/ui/waresdev/WareSrcWidgetCollection.cpp @@ -1172,6 +1172,36 @@ void WareSrcWidgetCollection::openWare(openfluid::ware::WareType Type, const QSt // ===================================================================== +void WareSrcWidgetCollection::duplicateWare(const QString& RefWarePath, const QString& NewWareName) +{ + // check if ware ID acceptable + if (!openfluid::tools::isValidWareID(NewWareName.toStdString())) //TOIMPL do this as validator in custom dialog + { + QMessageBox::critical(nullptr, tr("Duplicate simulator"), + tr("Error duplicating simulator: invalid characters in %1").arg(NewWareName)); + return; + } + try + { + std::string WarePath = openfluid::waresdev::WareSrcFactory::duplicateWare(NewWareName.toStdString(), + openfluid::tools::FilesystemPath(RefWarePath.toStdString()).dirname(), + RefWarePath.toStdString(), true + ); + + openWarePath(WarePath, false); + } + catch(const openfluid::base::FrameworkException& E) + { + QMessageBox::critical(nullptr, tr("Duplicate simulator"), tr("Error duplicating simulator %1: %2") + .arg(RefWarePath).arg(E.what())); + } +} + + +// ===================================================================== +// ===================================================================== + + void WareSrcWidgetCollection::deleteWare(const QString& WarePath) { if (QMessageBox::warning(QApplication::activeWindow(), tr("Delete ware"), diff --git a/src/openfluid/ui/waresdev/WareSrcWidgetCollection.hpp b/src/openfluid/ui/waresdev/WareSrcWidgetCollection.hpp index bb5e881e7..b603d4b45 100644 --- a/src/openfluid/ui/waresdev/WareSrcWidgetCollection.hpp +++ b/src/openfluid/ui/waresdev/WareSrcWidgetCollection.hpp @@ -361,6 +361,8 @@ class OPENFLUID_API WareSrcWidgetCollection: public QObject bool isBuildNoInstallMode(); + void duplicateWare(const QString& WarePath, const QString& NewWareName); + void deleteWare(const QString& WarePath); }; diff --git a/src/openfluid/utils/CMakeLists.txt b/src/openfluid/utils/CMakeLists.txt index 5ed248f7c..fc5586ad9 100644 --- a/src/openfluid/utils/CMakeLists.txt +++ b/src/openfluid/utils/CMakeLists.txt @@ -1,14 +1,26 @@ INCLUDE_DIRECTORIES(${GDAL_INCLUDE_DIR} ${CURL_INCLUDE_DIRS}) -# Hack for boost::process on Windows/MinGW only +SET(BOOST_COMPONENTS ) +SET(BOOST_LINK_LIBRARIES Boost::boost) +IF(Boost_VERSION_MINOR GREATER 85) + SET(BOOST_COMPONENTS ${BOOST_COMPONENTS} process) + SET(BOOST_LINK_LIBRARIES ${BOOST_LINK_LIBRARIES} Boost::process) +ENDIF() + + IF(MINGW) - FIND_PACKAGE(Boost REQUIRED filesystem) + SET(BOOST_COMPONENTS ${BOOST_COMPONENTS} filesystem system thread) + # Hack for boost::process on Windows/MinGW only IF(Boost_VERSION GREATER 107000) ADD_DEFINITIONS(-DBOOST_USE_WINDOWS_H -DWIN32_LEAN_AND_MEAN) ENDIF() + IF(Boost_VERSION_MINOR GREATER 85) + SET(BOOST_LINK_LIBRARIES ${BOOST_LINK_LIBRARIES} bcrypt) + ENDIF() ENDIF() - +FIND_PACKAGE(Boost REQUIRED ${BOOST_COMPONENTS}) +ADD_COMPILE_DEFINITIONS(Boost_VERSION_MINOR=${Boost_VERSION_MINOR}) SET(OPENFLUID_UTILS_CPP GDALHelpers.cpp Process.cpp @@ -46,12 +58,11 @@ TARGET_LINK_LIBRARIES(openfluid-utils openfluid-tools ${GDAL_LIBRARIES} ${CURL_LIBRARIES} - Boost::boost + ${BOOST_LINK_LIBRARIES} $<$:Boost::filesystem> $<$:ws2_32> # MinGW hack ) - INSTALL(TARGETS openfluid-utils RUNTIME DESTINATION ${OFBUILD_BIN_INSTALL_PATH} LIBRARY DESTINATION ${OFBUILD_LIB_INSTALL_PATH} diff --git a/src/openfluid/utils/GitProxy.cpp b/src/openfluid/utils/GitProxy.cpp index d92eda32a..5a991109c 100644 --- a/src/openfluid/utils/GitProxy.cpp +++ b/src/openfluid/utils/GitProxy.cpp @@ -138,6 +138,42 @@ bool GitProxy::isPathGitRepo(const std::string& Path) // ===================================================================== +int callRemoteProcess(openfluid::utils::Process::Command Cmd, std::string Context) +{ + openfluid::utils::Process P(Cmd); + P.run(); + if (P.getExitCode() == 0) + { + openfluid::utils::log::debug("Git", Context+" OK"); + } + else + { + openfluid::utils::log::debug("Git", Context+" out: "+openfluid::tools::join(P.stdOutLines(), "\n")); + openfluid::utils::log::error("Git", Context+" err: "+openfluid::tools::join(P.stdErrLines(), "\n")); + } + return P.getExitCode(); +} + + +// ===================================================================== +// ===================================================================== + + +int GitProxy::clone(const std::string& Path, const std::string& URL, const std::string& LocalName) +{ + openfluid::utils::Process::Command Cmd{ + .Program = m_ExecutablePath, + .Args = {"clone", "--recurse-submodules", URL, LocalName}, + .WorkDir = Path + }; + return callRemoteProcess(Cmd, "Git clone "+URL+" at "+Path+" as "+LocalName); +} + + +// ===================================================================== +// ===================================================================== + + const std::string GitProxy::getCurrentBranchName(const std::string& Path) { if (!canGetBranch()) @@ -166,4 +202,45 @@ const std::string GitProxy::getCurrentBranchName(const std::string& Path) } } + +// ===================================================================== +// ===================================================================== + + +int GitProxy::setRemote(const std::string RepoPath, const std::string RemoteUrl) +{ + if(isPathGitRepo(RepoPath)) + { + // already versioned, use 'set-url' + openfluid::utils::Process::Command Cmd{ + .Program = m_ExecutablePath, + .Args = {"remote", "set-url", "origin", RemoteUrl}, + .WorkDir = RepoPath + }; + return callRemoteProcess(Cmd, "Set remote for "+RepoPath); + } + else + { + // not versioned, use 'git init+git remote add origin' + openfluid::utils::Process::Command CmdInit{ + .Program = m_ExecutablePath, + .Args = {"init"}, + .WorkDir = RepoPath + }; + int InitReturn = callRemoteProcess(CmdInit, "(Add remote context) Init for "+RepoPath); + if (InitReturn != 0) + { + return InitReturn; + } + openfluid::utils::Process::Command Cmd{ + .Program = m_ExecutablePath, + .Args = {"remote", "add", "origin", RemoteUrl}, + .WorkDir = RepoPath + }; + return callRemoteProcess(Cmd, "Add remote for "+RepoPath);; + } + +} + + } } // namespaces diff --git a/src/openfluid/utils/GitProxy.hpp b/src/openfluid/utils/GitProxy.hpp index 47a97ffad..28f4a52e7 100644 --- a/src/openfluid/utils/GitProxy.hpp +++ b/src/openfluid/utils/GitProxy.hpp @@ -80,8 +80,12 @@ class OPENFLUID_API GitProxy : public ProgramProxy return (openfluid::tools::compareVersions(m_Version, "2.21") >= 0); } + int clone(const std::string& Path, const std::string& URL, const std::string& LocalName=""); + static const std::string getCurrentBranchName(const std::string& Path); + static int setRemote(const std::string RepoPath, const std::string RemoteUrl); + }; diff --git a/src/openfluid/utils/InternalLogger.cpp b/src/openfluid/utils/InternalLogger.cpp index 81667b351..b7579bdc0 100644 --- a/src/openfluid/utils/InternalLogger.cpp +++ b/src/openfluid/utils/InternalLogger.cpp @@ -118,6 +118,8 @@ void LoggingSystem::setup(bool DefaultAsFallback, std::string LogPath, bool Verb // FIXME should not be displayed in some cases // (interference risk with version detection for now, eg regex L142 in OpenFLUIDConfig.cmake.in) m_Logger.init(m_LogPath, false); + + // LoggingSystem::instance()->add(openfluid::tools::FileLogger::LogType::INFO_MSG, "Log", "Initiate logging"); } } diff --git a/src/openfluid/utils/Process.cpp b/src/openfluid/utils/Process.cpp index 83e8d6155..4159f3385 100644 --- a/src/openfluid/utils/Process.cpp +++ b/src/openfluid/utils/Process.cpp @@ -42,7 +42,21 @@ #include #include +#if (Boost_VERSION_MINOR > 85) && defined(OPENFLUID_OS_WINDOWS) +#include // FIXME check if useful or necessary, theorically useful to respect include order +#endif +#if (Boost_VERSION_MINOR > 85) +#include +#include +#include +#include +#include +#include +#include +#include +#else #include +#endif #include #include @@ -136,6 +150,107 @@ bool Process::run() try { +#if (Boost_VERSION_MINOR > 85) + std::ofstream StdOutFile; + std::ofstream StdErrFile; + std::string LineOut; + std::string LineErr; + std::unordered_map ProcessEnv; + boost::asio::io_context Ctx; + boost::asio::readable_pipe PipeOut{Ctx}; + boost::asio::readable_pipe PipeErr{Ctx}; + + bool SinkOutToFile = !m_Cmd.OutFile.empty(); + bool SinkErrToFile = !m_Cmd.ErrFile.empty(); + + // prepare environment + if (m_Env.Inherits) + { + for(const auto& InheritedVar : boost::process::v2::environment::current()) + { + ProcessEnv[InheritedVar.key().string()] = InheritedVar.value().string(); + } + } + + for (const auto& Var : m_Env.Vars) + { + ProcessEnv[Var.first] = Var.second; + } + // Hotfix for windows bug when work dir is empty + std::string WorkDir; + if (m_Cmd.WorkDir.empty()) + { + WorkDir = "."; // warning boost : If your path is relative, it may fail on posix, + //because the directory is changed before a call to execve. + } + else + { + WorkDir = m_Cmd.WorkDir; + } + + // boost::process::shell, <- not relevant in new system? + boost::process::v2::process Proc(Ctx, m_Cmd.Program, m_Cmd.Args, + boost::process::v2::process_stdio{nullptr, PipeOut, PipeErr}, + boost::process::v2::process_start_dir(WorkDir), + boost::process::v2::process_environment(ProcessEnv)); + + // if out is redirected, create out file + if (SinkOutToFile) + { + openfluid::tools::Path(openfluid::tools::Path(m_Cmd.OutFile).dirname()).makeDirectory(); + StdOutFile.open(m_Cmd.OutFile,std::ios::out); + } + + // if error is redirected, create error file + if (SinkErrToFile) + { + openfluid::tools::Path(openfluid::tools::Path(m_Cmd.ErrFile).dirname()).makeDirectory(); + StdErrFile.open(m_Cmd.ErrFile,std::ios::out); + } + + boost::system::error_code Ec; + boost::asio::read(PipeOut, boost::asio::dynamic_buffer(LineOut), Ec); + if (!Ec || (Ec == boost::asio::error::eof)) + { + std::cout << "Boost process reading error in out stream: " << Ec.message() << std::endl; + openfluid::utils::log::error("Process", "Boost process reading error in out stream"); + } + boost::asio::read(PipeErr, boost::asio::dynamic_buffer(LineErr), Ec); + if (!Ec || (Ec == boost::asio::error::eof)) + { + std::cout << "Boost process reading error in err stream: " << Ec.message() << std::endl; + openfluid::utils::log::error("Process", "Boost process reading error in err stream"); + } + + Proc.wait(); + for (const auto& L : openfluid::tools::split(LineOut, "\n")) + { + if (SinkOutToFile) + { + // if out is redirected, sink out lines in file + StdOutFile << L << "\n"; + } + else + { + m_OutLines.push_back(L); + } + } + + for (const auto& L : openfluid::tools::split(LineErr, "\n")) + { + if (SinkErrToFile) + { + // if out is redirected, sink out lines in file + StdErrFile << L << "\n"; + } + else + { + m_ErrLines.push_back(L); + } + } + + m_ExitCode = Proc.exit_code(); +#else boost::process::environment ProcessEnv; boost::process::ipstream StdOutStr; boost::process::ipstream StdErrStr; @@ -170,6 +285,12 @@ bool Process::run() WorkDir = m_Cmd.WorkDir; } + if (!openfluid::tools::Path(WorkDir).isDirectory()) + { + openfluid::utils::log::error("Process", "Working directory does not exist: "+WorkDir); + return false; + } + boost::process::child BPC(boost::process::exe = m_Cmd.Program, boost::process::args = m_Cmd.Args, boost::process::start_dir = WorkDir, @@ -223,17 +344,12 @@ bool Process::run() BPC.wait(); m_ExitCode = BPC.exit_code(); +#endif } - catch(const boost::process::process_error& E) + catch(const std::exception& E) { m_ErrorMsg = std::string(E.what()); - openfluid::utils::log::error("Process", std::string("Boost process error: ")+E.what()); - return false; - } - catch(...) - { - // TODO for logging purposes - openfluid::utils::log::error("Process", "Boost process error"); + openfluid::utils::log::error("Process", std::string("Boost process error: ")+m_ErrorMsg); return false; } @@ -285,6 +401,38 @@ int Process::system(const std::string& Program, const std::vector& int Process::system(const Command& Cmd, const Environment& Env) { +#if (Boost_VERSION_MINOR > 85) + boost::asio::io_context Ctx; + std::unordered_map ProcessEnv; + // prepare environment + if (Env.Inherits) + { + for(const auto& InheritedVar : boost::process::v2::environment::current()) + { + ProcessEnv[InheritedVar.key().string()] = InheritedVar.value().string(); + } + } + + for (const auto& Var : Env.Vars) + { + ProcessEnv[Var.first] = Var.second; + } + // Hotfix for windows bug when work dir is empty + std::string WorkDir; + if (Cmd.WorkDir.empty()) + { + WorkDir = "."; // warning boost : If your path is relative, it may fail on posix, + //because the directory is changed before a call to execve. + } + else + { + WorkDir = Cmd.WorkDir; + } + boost::process::v2::process Proc(Ctx, Cmd.Program, Cmd.Args, + boost::process::v2::process_start_dir(WorkDir), + boost::process::v2::process_environment(ProcessEnv)); + return Proc.wait(); +#else boost::process::environment ProcessEnv; // prepare environment @@ -315,6 +463,7 @@ int Process::system(const Command& Cmd, const Environment& Env) boost::process::args = Cmd.Args, boost::process::start_dir = WorkDir, ProcessEnv); +#endif } diff --git a/src/openfluid/utils/Process.hpp b/src/openfluid/utils/Process.hpp index f92b8df23..dbba8de6e 100644 --- a/src/openfluid/utils/Process.hpp +++ b/src/openfluid/utils/Process.hpp @@ -34,6 +34,7 @@ @file Process.hpp @author Jean-Christophe FABRE + @author Armel THÖNI */ @@ -44,6 +45,7 @@ #include #include #include +#include #include diff --git a/src/openfluid/ware/CMakeLists.txt b/src/openfluid/ware/CMakeLists.txt index 5b40082d0..a26ed0cca 100644 --- a/src/openfluid/ware/CMakeLists.txt +++ b/src/openfluid/ware/CMakeLists.txt @@ -1,5 +1,5 @@ -SET(OPENFLUID_WARE_CPP WareIssues.cpp SimulatorSignature.cpp +SET(OPENFLUID_WARE_CPP WareIssues.cpp WareSignature.cpp PluggableWare.cpp SimulationDrivenWare.cpp SimulationInspectorWare.cpp SimulationContributorWare.cpp PluggableSimulator.cpp PluggableObserver.cpp WareParamsTree.cpp @@ -9,7 +9,7 @@ SET(OPENFLUID_WARE_CPP WareIssues.cpp SimulatorSignature.cpp SET(OPENFLUID_WARE_HPP WareIssues.hpp WareSignature.hpp SimulatorSignature.hpp ObserverSignature.hpp PluggableWare.hpp SimulationDrivenWare.hpp SimulationInspectorWare.hpp SimulationContributorWare.hpp PluggableSimulator.hpp PluggableObserver.hpp - TypeDefs.hpp + TypeDefs.hpp DataItemUtils.hpp LoopMacros.hpp ThreadedLoopMacros.hpp WareException.hpp WareRNG.hpp diff --git a/src/openfluid/ware/DataItemUtils.hpp b/src/openfluid/ware/DataItemUtils.hpp new file mode 100644 index 000000000..e72ad429d --- /dev/null +++ b/src/openfluid/ware/DataItemUtils.hpp @@ -0,0 +1,81 @@ +/* + + This file is part of OpenFLUID software + Copyright(c) 2007, INRA - Montpellier SupAgro + + + == GNU General Public License Usage == + + OpenFLUID is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFLUID is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OpenFLUID. If not, see . + + + == Other Usage == + + Other Usage means a use of OpenFLUID that is inconsistent with the GPL + license, and requires a written agreement between You and INRA. + Licensees for Other Usage of OpenFLUID may use this file in accordance + with the terms contained in the written agreement between You and INRA. + +*/ + + +/** + @file DataItemUtils.hpp + + @author Armel THÖNI +*/ + +#pragma once + + +#include + +#include + + +namespace openfluid { namespace ware { + +// contains minimal functions used by signature data item constructors + + +/** + Transmits a given variable name to container without changing type + @param[in] VO the string to use + @param[in] V the string to populate + @param[in] T the type to change (ignored) + @return true always +*/ +inline bool OPENFLUID_API identityExtractor(const std::string& VO, std::string& V, openfluid::core::Value::Type& /*T*/) +{ + V = VO; + return true; +} + + +// ===================================================================== +// ===================================================================== + + +/** + Checks whether a string is empty + @param[in] Str the string to check + @return true if the string is not empty +*/ +inline bool OPENFLUID_API isNonEmpty(const std::string& Str) +{ + return Str.length() > 0; +} + + +} } // namespaces diff --git a/src/openfluid/ware/SimulatorSignature.hpp b/src/openfluid/ware/SimulatorSignature.hpp index ba3d16c6a..979ce4de2 100644 --- a/src/openfluid/ware/SimulatorSignature.hpp +++ b/src/openfluid/ware/SimulatorSignature.hpp @@ -34,8 +34,10 @@ @file SimulatorSignature.hpp @author Jean-Christophe FABRE + @author Armel THÖNI */ + #ifndef __OPENFLUID_WARE_SIMULATORSIGNATURE_HPP__ #define __OPENFLUID_WARE_SIMULATORSIGNATURE_HPP__ @@ -46,11 +48,34 @@ #include #include #include +#include #include namespace openfluid { namespace ware { +class OPENFLUID_API SignatureVariableItem : public SignatureSpatialDataItem +{ + public: + + SignatureVariableItem() : SignatureSpatialDataItem() + { } + + SignatureVariableItem(const std::string& N, const openfluid::core::UnitsClass_t& U, + const std::string& D, const std::string& SI) : + SignatureSpatialDataItem(N,U,D,SI,openfluid::tools::extractVariableNameAndType) + { } + + SignatureVariableItem(const std::string& N, const openfluid::core::UnitsClass_t& U, + const std::string& D, const std::string& SI, openfluid::core::Value::Type T) : + SignatureSpatialDataItem(N,U,D,SI,T,openfluid::tools::isValidVariableName) + { } +}; + + +// ===================================================================== +// ===================================================================== + /** Class for storage of the definition of the data handled by the simulator. This is part of the signature. diff --git a/src/openfluid/ware/SimulatorSignature.cpp b/src/openfluid/ware/WareSignature.cpp similarity index 76% rename from src/openfluid/ware/SimulatorSignature.cpp rename to src/openfluid/ware/WareSignature.cpp index 5e0d49124..440ef322a 100644 --- a/src/openfluid/ware/SimulatorSignature.cpp +++ b/src/openfluid/ware/WareSignature.cpp @@ -31,28 +31,30 @@ /** - @file SimulatorSignature.cpp + @file WareSignature.cpp @author Jean-Christophe FABRE - */ + @author Armel THÖNI +*/ -#include -#include -#include +#include +#include +#include +#include +#include namespace openfluid { namespace ware { - -SignatureDataItem::SignatureDataItem(const std::string& N, const std::string& D,const std::string& SI, - openfluid::core::Value::Type T): - Name(N),Description(D),SIUnit(SI),DataType(T) +SignatureDataItem::SignatureDataItem(const std::string& N, const std::string& D, const std::string& SI, + std::function Extractor): + Description(D),SIUnit(SI) { - if (!openfluid::tools::isValidVariableName(N)) + if (!Extractor(N,this->Name,this->DataType)) { throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION, - "Variable " + N + " is not well formatted."); + "Variable " + N + " with optional type is not well formatted."); } } @@ -61,15 +63,15 @@ SignatureDataItem::SignatureDataItem(const std::string& N, const std::string& D, // ===================================================================== -SignatureDataItem::SignatureDataItem(const std::string& N, const std::string& D, const std::string& SI): - Description(D),SIUnit(SI) +SignatureDataItem::SignatureDataItem(const std::string& N, const std::string& D, const std::string& SI, + openfluid::core::Value::Type T, std::function Validator): + Name(N),Description(D),SIUnit(SI),DataType(T) { - if (!openfluid::tools::extractVariableNameAndType(N,this->Name,this->DataType)) + if (!Validator(N)) { throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION, - "Variable " + N + " with optional type is not well formatted."); + "Variable " + N + " is not well formatted."); } } - -} } //namespaces +} }; diff --git a/src/openfluid/ware/WareSignature.hpp b/src/openfluid/ware/WareSignature.hpp index 4d54cb1a4..0993be7b1 100644 --- a/src/openfluid/ware/WareSignature.hpp +++ b/src/openfluid/ware/WareSignature.hpp @@ -40,8 +40,10 @@ #include #include #include +#include #include #include +#include #include @@ -241,10 +243,12 @@ class OPENFLUID_API SignatureDataItem SignatureDataItem() { } - SignatureDataItem(const std::string& N, const std::string& D, const std::string& SI); + SignatureDataItem(const std::string& N, const std::string& D, const std::string& SI, + std::function Extractor=openfluid::ware::identityExtractor); SignatureDataItem(const std::string& N, const std::string& D, const std::string& SI, - openfluid::core::Value::Type T); + openfluid::core::Value::Type T, std::function Validator=openfluid::ware::isNonEmpty); }; @@ -265,13 +269,16 @@ class OPENFLUID_API SignatureSpatialDataItem : public SignatureDataItem { } SignatureSpatialDataItem(const std::string& N, const openfluid::core::UnitsClass_t& U, - const std::string& D, const std::string& SI) : - SignatureDataItem(N,D,SI),UnitsClass(U) + const std::string& D, const std::string& SI, + std::function Extractor=openfluid::ware::identityExtractor): + SignatureDataItem(N,D,SI,Extractor),UnitsClass(U) { } SignatureSpatialDataItem(const std::string& N, const openfluid::core::UnitsClass_t& U, - const std::string& D, const std::string& SI, openfluid::core::Value::Type T) : - SignatureDataItem(N,D,SI,T),UnitsClass(U) + const std::string& D, const std::string& SI, openfluid::core::Value::Type T, + std::function Validator=openfluid::ware::isNonEmpty): + SignatureDataItem(N,D,SI,T,Validator),UnitsClass(U) { } }; diff --git a/src/openfluid/waresdev/SimulatorSignatureSerializer.cpp b/src/openfluid/waresdev/SimulatorSignatureSerializer.cpp index cbaa8ecb1..7a0a8a690 100644 --- a/src/openfluid/waresdev/SimulatorSignatureSerializer.cpp +++ b/src/openfluid/waresdev/SimulatorSignatureSerializer.cpp @@ -69,12 +69,12 @@ void SimulatorSignatureSerializer::unserializeVariablesFromJSON(const openfluid: { if (Json.contains("produced")) { - Sign.SimulatorHandledData.ProducedVars = DataJsonConverter::readSpatialDataListFromJSON(Json.at("produced")); + Sign.SimulatorHandledData.ProducedVars = DataJsonConverter::readVariableListFromJSON(Json.at("produced")); } if (Json.contains("updated")) { - Sign.SimulatorHandledData.UpdatedVars = DataJsonConverter::readSpatialDataListFromJSON(Json.at("updated")); + Sign.SimulatorHandledData.UpdatedVars = DataJsonConverter::readVariableListFromJSON(Json.at("updated")); } } diff --git a/src/openfluid/waresdev/WareCppWriterHelpers.hpp b/src/openfluid/waresdev/WareCppWriterHelpers.hpp index 8166145ec..efabe6ebc 100644 --- a/src/openfluid/waresdev/WareCppWriterHelpers.hpp +++ b/src/openfluid/waresdev/WareCppWriterHelpers.hpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -332,15 +333,16 @@ struct CppWriter { struct DataJsonConverter { - static openfluid::ware::SignatureDataItem readDataItemFromJSON(const openfluid::thirdparty::json& Item) + static openfluid::ware::SignatureDataItem readDataItemFromJSON(const openfluid::thirdparty::json& Item, + std::function Validator=openfluid::ware::isNonEmpty) { openfluid::ware::SignatureDataItem Data; Data.Name = Item.value("name",""); - if (!openfluid::tools::isValidVariableName(Data.Name)) + if (!Validator(Data.Name)) { - throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,"Missing or invalid data name"); + throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,"Missing or invalid data name: "+Data.Name); } Data.Description = Item.value("description",""); @@ -551,14 +553,16 @@ struct DataJsonConverter // ===================================================================== - static openfluid::ware::SignatureSpatialDataItem readSpatialDataItemFromJSON(const openfluid::thirdparty::json& Item) + static openfluid::ware::SignatureSpatialDataItem readSpatialDataItemFromJSON(const openfluid::thirdparty::json& Item, + std::function Validator=openfluid::ware::isNonEmpty) { openfluid::ware::SignatureSpatialDataItem Data; Data.Name = Item.value("name",""); - if (!openfluid::tools::isValidVariableName(Data.Name)) + if (!Validator(Data.Name)) { - throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION,"Missing or invalid data name"); + throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION, + "Missing or invalid spatial data name: "+Data.Name); } Data.UnitsClass = Item.value("unitsclass",""); @@ -586,7 +590,8 @@ struct DataJsonConverter static std::vector - readSpatialDataListFromJSON(const openfluid::thirdparty::json& Json) + readSpatialDataListFromJSON(const openfluid::thirdparty::json& Json, + std::function Validator=openfluid::ware::isNonEmpty) { std::vector List; @@ -594,7 +599,7 @@ struct DataJsonConverter { for (const auto& I : Json) { - List.push_back(readSpatialDataItemFromJSON(I)); + List.push_back(readSpatialDataItemFromJSON(I, Validator)); } } @@ -606,6 +611,17 @@ struct DataJsonConverter // ===================================================================== + static std::vector + readVariableListFromJSON(const openfluid::thirdparty::json& Json) + { + return readSpatialDataListFromJSON(Json, openfluid::tools::isValidVariableName); + } + + + // ===================================================================== + // ===================================================================== + + static void unserializeReadAttributesFromJSON(const openfluid::thirdparty::json& Json, openfluid::ware::DataWareSignature& Sign) { @@ -631,12 +647,12 @@ struct DataJsonConverter { if (Json.contains("used")) { - Sign.HandledData.UsedVars = readSpatialDataListFromJSON(Json.at("used")); + Sign.HandledData.UsedVars = readVariableListFromJSON(Json.at("used")); } if (Json.contains("required")) { - Sign.HandledData.RequiredVars = readSpatialDataListFromJSON(Json.at("required")); + Sign.HandledData.RequiredVars = readVariableListFromJSON(Json.at("required")); } } diff --git a/src/openfluid/waresdev/WareSrcChecker.cpp b/src/openfluid/waresdev/WareSrcChecker.cpp index 2f7a6479c..266acae9e 100644 --- a/src/openfluid/waresdev/WareSrcChecker.cpp +++ b/src/openfluid/waresdev/WareSrcChecker.cpp @@ -144,7 +144,8 @@ void WareSrcChecker::updateWithPedanticCheck(ReportingData& RepData) for (const auto& Pair : CategoryByWaretype) { - if (Json.value(Pair.first, openfluid::thirdparty::json::array()) != openfluid::thirdparty::json::array()) + if (Json.value(Pair.first, openfluid::thirdparty::json::array()) != openfluid::thirdparty::json::array() && + Json.at(Pair.first).contains("data")) { openfluid::thirdparty::json DataJson = Json.at(Pair.first).at("data"); if (DataJson != openfluid::thirdparty::json::array()) diff --git a/src/openfluid/waresdev/WareSrcEnquirer.cpp b/src/openfluid/waresdev/WareSrcEnquirer.cpp index ef52bc16a..473fa69be 100644 --- a/src/openfluid/waresdev/WareSrcEnquirer.cpp +++ b/src/openfluid/waresdev/WareSrcEnquirer.cpp @@ -163,6 +163,14 @@ WareSrcEnquirer::WarePathInfo WareSrcEnquirer::getWareInfoFromPath(const std::st { Info.IsInExamples = true; + fillInfo(openfluid::base::Environment::getUserExamplesDir(),Info); + } + // path is in examples observers directory + else if (openfluid::tools::Path(openfluid::base::Environment::getUserExampleObserversDir()) + .contains(Info.AbsolutePath)) + { + Info.IsInExamples = true; + fillInfo(openfluid::base::Environment::getUserExamplesDir(),Info); } } diff --git a/src/openfluid/waresdev/WareSrcFactory.cpp b/src/openfluid/waresdev/WareSrcFactory.cpp index e0d12bfd9..f9a1ae07f 100644 --- a/src/openfluid/waresdev/WareSrcFactory.cpp +++ b/src/openfluid/waresdev/WareSrcFactory.cpp @@ -44,11 +44,16 @@ #include #include #include +#include #include +#include #include #include #include +#include +#include #include +#include "WareSrcFactory.hpp" namespace openfluid { namespace waresdev { @@ -372,6 +377,118 @@ std::string WareSrcFactory::createBuilderext(const openfluid::builderext::Builde // ===================================================================== +void replaceInFolder(std::string Folder, std::string PreviousWareId, std::string NewWareId, bool AcceptAll=false) +{ + std::string Strategy = (AcceptAll ? "always" : "ask"); + for (const auto& PosByFile : openfluid::tools::searchInFolder(Folder, PreviousWareId)) + { + const auto PathParts = openfluid::tools::FilesystemPath(PosByFile.first).split(); + + // ignore .git folder and '_' prefixed folders + const auto WareIdPos = std::find(PathParts.begin(), PathParts.end(), NewWareId); + bool IgnoredSubDir = false; + for(auto It = WareIdPos; It < PathParts.end(); It++ ) + { + if ((*It)[0] == '_') + { + IgnoredSubDir = true; + } + } + + if (std::find(PathParts.begin(), PathParts.end(), ".git") == PathParts.end() && + !IgnoredSubDir) + { + std::cout << "In file " << PosByFile.first << std::endl; //TOIMPL better display management + const auto FileObj = openfluid::tools::Path::fromStdPath(PosByFile.first); + std::string FileContent = openfluid::tools::Filesystem::readFile(FileObj); + // do from end to begin to avoid char pos shift + std::set::reverse_iterator Rit; + for (Rit = PosByFile.second.rbegin(); Rit != PosByFile.second.rend(); Rit++) + { + const auto& Pos = *Rit; + //TOIMPL better line display: one line before and one line after + std::string DisplayedContext = FileContent.substr(std::max((std::size_t)0,(std::max(Pos, + (std::size_t)10)-10)), + std::min(PreviousWareId.size()+20, FileContent.size()-Pos)); + + std::size_t BeforeBegin = std::max((std::size_t)0,(std::max(Pos, (std::size_t)10)-10)); + std::size_t AfterBegin = Pos+PreviousWareId.size(); + std::size_t AfterEnd = std::min(Pos+PreviousWareId.size()+20, FileContent.size()); + auto Before = FileContent.substr(BeforeBegin, Pos-BeforeBegin); + auto After = FileContent.substr(AfterBegin, AfterEnd-AfterBegin); + std::cout << Before << "\033[1;31m"<< FileContent.substr(Pos, PreviousWareId.size()) << + "\033[0m" << After << std::endl; + bool Apply = false; + if (Strategy == "ask") + { + std::cout << "Replace this occurence? (yes/no/Always/Never) "; + std::string Choice; + std::cin >> Choice; + if (Choice.size() < 1) + { + // no content <=> no + } + else if (Choice[0] == 'y') + { + Apply = true; + } + else if (Choice[0] == 'A') + { + Apply = true; + Strategy = "always"; + } + else if (Choice[0] == 'N') + { + Strategy = "never"; + } + } + else if (Strategy == "always") + { + Apply = true; + } + if (Apply) + { + FileContent = openfluid::tools::replace_once(FileContent, PreviousWareId, NewWareId, Pos); + openfluid::tools::Filesystem::writeFile(FileContent,FileObj); + } + } + } + } +} + + +// ===================================================================== +// ===================================================================== + + +std::string WareSrcFactory::duplicateWare(const std::string ID, const std::string& ParentPath, + const std::string& OriginDir, bool AcceptAll) +{ + std::string CleanedOriginDir = openfluid::tools::FilesystemPath::removeTrailingSeparators(OriginDir); + + auto WareSrcPathObj = getSrcPathObject(ID,ParentPath,true); + + // copy directory + std::string PreviousWareId = openfluid::tools::Path(CleanedOriginDir).filename(); + + // Check if directory does not already exist + if (WareSrcPathObj.exists()) + { + throw openfluid::base::FrameworkException(OPENFLUID_CODE_LOCATION, + "Ware duplication aborted: target location already exists"); + } + WareSrcPathObj.makeDirectory(); + openfluid::tools::Filesystem::copyDirectoryContent(CleanedOriginDir, WareSrcPathObj.toGeneric()); + + replaceInFolder(WareSrcPathObj.toGeneric(), PreviousWareId, ID, AcceptAll); + return WareSrcPathObj.toGeneric(); +} + + +// ===================================================================== +// ===================================================================== + + std::string getDeclarationStringFromVarType(openfluid::core::Value::Type VarType) { if (VarType == openfluid::core::Value::NONE) diff --git a/src/openfluid/waresdev/WareSrcFactory.hpp b/src/openfluid/waresdev/WareSrcFactory.hpp index 0017f5fca..90f4b5483 100644 --- a/src/openfluid/waresdev/WareSrcFactory.hpp +++ b/src/openfluid/waresdev/WareSrcFactory.hpp @@ -111,6 +111,8 @@ class OPENFLUID_API WareSrcFactory const std::string& ParentPath, bool WithIDSubDir = true); + static std::string duplicateWare(const std::string ID, const std::string& ParentPath, + const std::string& OriginDir, bool AcceptAll=false); }; diff --git a/src/openfluid/waresdev/WareSrcHelpers.cpp b/src/openfluid/waresdev/WareSrcHelpers.cpp index 6a973d789..88526d3c9 100644 --- a/src/openfluid/waresdev/WareSrcHelpers.cpp +++ b/src/openfluid/waresdev/WareSrcHelpers.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -119,4 +120,33 @@ std::map initializeConfigureVariables() } +// ===================================================================== +// ===================================================================== + + +int OPENFLUID_API cloneWare(const std::string& SourceURL, const std::string& SourceType, const std::string& ParentPath, + const std::string& WareID, const std::string& WareType) +{ + std::string GitURL; + if (SourceType=="hub") + { + std::string HubURL = SourceURL; + if (HubURL.length() == 0) + { + // try to deduce hub info from openfluid config (only working with fluidhub-like URLs) + openfluid::base::Environment::init(); + const auto Settings = openfluid::tools::SettingsBackend(openfluid::base::Environment::getSettingsFile()); + HubURL = Settings.getValue("/waresdev/ui/import/hub/url").get(); + } + GitURL = HubURL.substr(0,HubURL.length()-5)+"/git-service/wares/"+WareType+"s/"+WareID; + } + else + { + GitURL = SourceURL; + } + openfluid::utils::GitProxy Git; + return Git.clone(ParentPath, GitURL, WareID); +} + + } } // namespaces diff --git a/src/openfluid/waresdev/WareSrcHelpers.hpp b/src/openfluid/waresdev/WareSrcHelpers.hpp index 0f11fc3e6..2c9706f11 100644 --- a/src/openfluid/waresdev/WareSrcHelpers.hpp +++ b/src/openfluid/waresdev/WareSrcHelpers.hpp @@ -51,6 +51,7 @@ #include #include #include +#include #include @@ -212,6 +213,22 @@ bool OPENFLUID_API isWareInCurrentWorkspace(const std::string& WarePath); std::map OPENFLUID_API initializeConfigureVariables(); +// ===================================================================== +// ===================================================================== + + +/** + Clones a ware from remote git or hub URL + @param[in] SourceURL the ware path, the hub or git adress, may be optional + @param[in] SourceType either "hub" or "git" depending on source type + @param[in] ParentPath the path where clone will be run + @param[in] WareID the ware path, used for both local name and remote search on hub + @param[in] WareType the ware type, required for hub clone + @return exitcode of clone operation (0 means success) +*/ +int OPENFLUID_API cloneWare(const std::string& SourceURL, const std::string& SourceType="hub", + const std::string& ParentPath=".", const std::string& WareID="", + const std::string& WareType=""); } } // namespaces diff --git a/src/openfluid/waresdev/WareSrcMigrator.cpp b/src/openfluid/waresdev/WareSrcMigrator.cpp index 4015c7f4d..d6019cddb 100644 --- a/src/openfluid/waresdev/WareSrcMigrator.cpp +++ b/src/openfluid/waresdev/WareSrcMigrator.cpp @@ -963,12 +963,14 @@ WareSrcMigrator::processCMakeFiles(const WareSrcMigrator::WareMigrationInfo& Inf mp_Listener->stageMessage("processing " + openfluid::config::WARESDEV_SRC_CMAKESTDFILE); auto CMakeWareVar = Info.CMakeDefaultWareVar; + std::vector CMakeContentLines; auto CMakeFileObj = m_SrcPathObj.fromThis(openfluid::config::WARESDEV_SRC_CMAKESTDFILE); if (CMakeFileObj.isFile()) { // search for correct variable name auto CMakeContent = openfluid::tools::Filesystem::readFile(CMakeFileObj); + CMakeContentLines = getFileLines(CMakeFileObj); if (!CMakeContent.empty()) { // search for default @@ -1020,6 +1022,36 @@ WareSrcMigrator::processCMakeFiles(const WareSrcMigrator::WareMigrationInfo& Inf return (L.empty() || L[0] == '#'); }), ConfigLines.end()); + + std::vector CustomContentLines; + bool IsCustomCMakeContent = false; + for (std::vector::size_type i=0; i DefaultLineBegins = {"PROJECT(", "CMAKE_MINIMUM_REQUIRED(", "INCLUDE(CMake.in.config)", + "FIND_PACKAGE(OpenFLUIDHelpers REQUIRED)", "OPENFLUID_ADD_"}; + bool IsDefault = false; + for (const auto& Begin : DefaultLineBegins) + { + if (CMakeContentLines[i].substr(0,Begin.length()) == Begin) + { + IsDefault = true; + } + } + if (!IsDefault) + { + CustomContentLines.push_back(CMakeContentLines[i]); + if (CMakeContentLines[i].size() > 0) + { + IsCustomCMakeContent = true; + } + } + } + if (IsCustomCMakeContent) + { + ConfigLines.push_back(""); + ConfigLines.push_back("# Custom content from main CMakeLists.txt"); + ConfigLines.insert(ConfigLines.end(), CustomContentLines.begin(), CustomContentLines.end() ); + } const auto ConfigContent = openfluid::tools::join(ConfigLines," "); std::map ConfigVariables; diff --git a/src/openfluid/waresdev/tests/SignatureCommon.hpp b/src/openfluid/waresdev/tests/SignatureCommon.hpp index aef8e841b..7c7cdc73e 100644 --- a/src/openfluid/waresdev/tests/SignatureCommon.hpp +++ b/src/openfluid/waresdev/tests/SignatureCommon.hpp @@ -243,7 +243,7 @@ bool compareJSONRecursive(openfluid::thirdparty::json& Json1, unsigned int Json2Size = Json2.size(); if(WithCheck) { - BOOST_CHECK_EQUAL(Json1Size, Json2Size); + BOOST_REQUIRE_EQUAL(Json1Size, Json2Size); } IsEqual = IsEqual && Json1Size == Json2Size; diff --git a/src/openfluid/waresdev/tests/SimulatorSignatureSerializer_TEST.cpp b/src/openfluid/waresdev/tests/SimulatorSignatureSerializer_TEST.cpp index 0a076d3c5..ab0c2fe6b 100644 --- a/src/openfluid/waresdev/tests/SimulatorSignatureSerializer_TEST.cpp +++ b/src/openfluid/waresdev/tests/SimulatorSignatureSerializer_TEST.cpp @@ -94,7 +94,6 @@ BOOST_AUTO_TEST_CASE(read_json) auto Sign = openfluid::waresdev::SimulatorSignatureSerializer() .readFromJSONFile(WorkPath.fromThis(openfluid::config::WARESDEV_WAREMETA_FILE).toGeneric()); - compareSignatures(Sign,SignRef,"read_json"); } diff --git a/src/openfluid/waresdev/tests/SimulatorSignatureUtils.hpp b/src/openfluid/waresdev/tests/SimulatorSignatureUtils.hpp index 430efbf03..ff5ec47e2 100644 --- a/src/openfluid/waresdev/tests/SimulatorSignatureUtils.hpp +++ b/src/openfluid/waresdev/tests/SimulatorSignatureUtils.hpp @@ -82,13 +82,16 @@ openfluid::ware::SimulatorSignature getRefSignature() Sign.Dependencies["otherLib"] = ">=1.0"; Sign.HandledData.UsedParams.push_back({"coeff","coefficient",""}); + Sign.HandledData.UsedParams.push_back({"coeff 2","coefficient with a space",""}); + Sign.HandledData.UsedParams.push_back({"coeff&3","coefficient with weïrd char",""}); Sign.HandledData.RequiredParams.push_back({"speedl","speed limit","m/s",openfluid::core::Value::DOUBLE}); Sign.HandledData.RequiredParams.push_back({"forcefield","field of force","n/m2",openfluid::core::Value::MATRIX}); Sign.HandledData.UsedExtraFiles = {"observed.csv","simulated.csv","randomized.csv"}; Sign.HandledData.RequiredExtraFiles = {"forced_data1.csv","forced_data2.csv",}; - Sign.HandledData.UsedAttributes.push_back({"venue_capacity[integer]","YU","the venue capacity",""}); + Sign.HandledData.UsedAttributes.push_back({"venue_capacity","YU","the venue capacity","", + openfluid::core::Value::INTEGER}); Sign.HandledData.UsedAttributes.push_back({"venue_volume","YU","the venue volume","m3s"}); Sign.HandledData.RequiredAttributes.push_back( {"stage_area","ZU","the stage area","m2",openfluid::core::Value::DOUBLE}); @@ -97,7 +100,8 @@ openfluid::ware::SimulatorSignature getRefSignature() Sign.SimulatorHandledData.ProducedVars.push_back( {"venue.band.music.instruments","YU","the music","db",openfluid::core::Value::VECTOR}); - Sign.SimulatorHandledData.ProducedVars.push_back({"venue.stage.light.system[map]","YU","the light","lm"}); + Sign.SimulatorHandledData.ProducedVars.push_back({"venue.stage.light.system","YU","the light","lm", + openfluid::core::Value::MAP}); Sign.SimulatorHandledData.UpdatedVars.push_back( {"venue.temperature.attendance","ZU","the venue temperature","K",openfluid::core::Value::DOUBLE}); Sign.HandledData.RequiredVars.push_back(