├── .github └── workflows │ └── fmu_manipulation_toolbox-package.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── container ├── CMakeLists.txt ├── README.md ├── build.bat ├── container.c ├── container.h ├── fmu.c ├── fmu.h ├── hash.c ├── hash.h ├── library.c ├── library.h ├── logger.c ├── logger.h ├── profile.c ├── profile.h ├── thread.c └── thread.h ├── doc ├── FMUContainer.png ├── README.md ├── checker.md ├── container-json.png ├── container.md ├── example.py ├── gui.png ├── remoting.md └── routing.png ├── fmi ├── fmi2FunctionTypes.h ├── fmi2Functions.h └── fmi2TypesPlatform.h ├── fmu_manipulation_toolbox ├── __init__.py ├── __main__.py ├── assembly.py ├── checker.py ├── cli.py ├── fmu_container.py ├── fmu_operations.py ├── gui.py ├── gui_style.py ├── help.py ├── resources │ ├── checkbox-checked-disabled.png │ ├── checkbox-checked-hover.png │ ├── checkbox-checked.png │ ├── checkbox-unchecked-disabled.png │ ├── checkbox-unchecked-hover.png │ ├── checkbox-unchecked.png │ ├── container.png │ ├── darwin64 │ │ └── .gitkeep │ ├── drop_fmu.png │ ├── fmi-2.0 │ │ ├── fmi2Annotation.xsd │ │ ├── fmi2AttributeGroups.xsd │ │ ├── fmi2ModelDescription.xsd │ │ ├── fmi2ScalarVariable.xsd │ │ ├── fmi2Type.xsd │ │ ├── fmi2Unit.xsd │ │ └── fmi2VariableDependency.xsd │ ├── fmu.png │ ├── fmu_manipulation_toolbox.png │ ├── help.png │ ├── icon-round.png │ ├── icon.png │ ├── icon_fmu.png │ ├── license.txt │ ├── linux32 │ │ └── .gitkeep │ ├── linux64 │ │ └── .gitkeep │ ├── mask.png │ ├── model.png │ ├── win32 │ │ └── .gitkeep │ └── win64 │ │ └── .gitkeep └── version.py ├── package.py ├── remoting ├── CMakeLists.txt ├── README.md ├── build.bat ├── client.c ├── client.h ├── communication.c ├── communication.h ├── config.h.in ├── gen_sizeof.py ├── process.c ├── process.h ├── remote.c ├── remote.h ├── server.c ├── server.h ├── test_server.c ├── test_sizeof.c ├── types.txt └── version.h ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── containers ├── arch │ ├── REF-flat-dump.json │ ├── REF-hierarchical-dump.json │ ├── REF-reversed-dump.json │ ├── flat.json │ ├── gain.fmu │ ├── hierarchical.json │ ├── integrate.fmu │ ├── nested.json │ ├── reversed.json │ ├── sine.fmu │ └── subdir │ │ └── gain2.fmu ├── bouncing_ball │ ├── REF-bouncing-profiling.json │ ├── REF-bouncing.json │ ├── REF-container-profiling.txt │ ├── REF-container.txt │ ├── REF-modelDescription-profiling.xml │ ├── bb_position.fmu │ ├── bb_velocity.fmu │ ├── bouncing-profiling.csv │ ├── bouncing.csv │ ├── bouncing_auto.csv │ └── bouncing_unlinked.csv ├── ssp │ ├── REF-bouncing-dump.json │ └── bouncing.ssp └── start │ ├── REF-container.txt │ ├── REF-modelDescription.xml │ ├── slx.fmu │ └── slx.json ├── operations ├── REF-bouncing_ball-keeponly.csv ├── REF-bouncing_ball-modified.csv ├── REF-bouncing_ball-no-tl.csv ├── REF-bouncing_ball-removed.csv ├── REF-bouncing_ball-renamed.csv ├── REF-bouncing_ball.csv ├── bouncing_ball-modified.csv └── bouncing_ball.fmu └── test_suite.py /.github/workflows/fmu_manipulation_toolbox-package.yml: -------------------------------------------------------------------------------- 1 | # This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. 2 | # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml 3 | name: Release FMUManipulationToolbox 4 | 5 | on: 6 | push: 7 | 8 | permissions: 9 | id-token: write 10 | 11 | jobs: 12 | remoting-windows32: 13 | runs-on: windows-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Set reusable strings 18 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 19 | id: strings 20 | shell: bash 21 | run: | 22 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 23 | 24 | - name: Configure CMake 25 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 26 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 27 | run: > 28 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 29 | -DCMAKE_CXX_COMPILER=cl 30 | -DCMAKE_C_COMPILER=cl 31 | -DCMAKE_BUILD_TYPE=Release 32 | -A Win32 33 | -S ${{ github.workspace }}/remoting 34 | 35 | - name: Build 36 | # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 37 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release 38 | - name: Archive Artifacts 39 | uses: actions/upload-artifact@master 40 | with: 41 | name: remoting-win32 42 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/win32/ 43 | retention-days: 1 44 | remoting-windows64: 45 | runs-on: windows-latest 46 | steps: 47 | - uses: actions/checkout@v4 48 | 49 | - name: Set reusable strings 50 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 51 | id: strings 52 | shell: bash 53 | run: | 54 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 55 | 56 | - name: Configure CMake 57 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 58 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 59 | run: > 60 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 61 | -DCMAKE_CXX_COMPILER=cl 62 | -DCMAKE_C_COMPILER=cl 63 | -DCMAKE_BUILD_TYPE=Release 64 | -A x64 65 | -S ${{ github.workspace }}/remoting 66 | 67 | - name: Build 68 | # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 69 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release 70 | - name: Archive Artifacts 71 | uses: actions/upload-artifact@master 72 | with: 73 | name: remoting-win64 74 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/win64 75 | retention-days: 1 76 | remoting-linux32: 77 | runs-on: ubuntu-latest 78 | steps: 79 | - uses: actions/checkout@v4 80 | 81 | - name: Set reusable strings 82 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 83 | id: strings 84 | shell: bash 85 | run: | 86 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 87 | 88 | - name: Install needed packets 89 | run: | 90 | sudo apt-get update 91 | sudo apt-get install gcc-multilib 92 | 93 | - name: Configure CMake 94 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 95 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 96 | run: > 97 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 98 | -DCMAKE_CXX_COMPILER=g++ 99 | -DCMAKE_C_COMPILER=gcc 100 | -DCMAKE_BUILD_TYPE=Release 101 | -DBUILD_32=ON 102 | -S ${{ github.workspace }}/remoting 103 | 104 | - name: Build 105 | # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 106 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release 107 | - name: Archive Artifacts 108 | uses: actions/upload-artifact@master 109 | with: 110 | name: remoting-linux32 111 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/linux32 112 | retention-days: 1 113 | remoting-linux64: 114 | runs-on: ubuntu-latest 115 | steps: 116 | - uses: actions/checkout@v4 117 | 118 | - name: Set reusable strings 119 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 120 | id: strings 121 | shell: bash 122 | run: | 123 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 124 | 125 | - name: Configure CMake 126 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 127 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 128 | run: > 129 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 130 | -DCMAKE_CXX_COMPILER=g++ 131 | -DCMAKE_C_COMPILER=gcc 132 | -DCMAKE_BUILD_TYPE=Release 133 | -S ${{ github.workspace }}/remoting 134 | 135 | - name: Build 136 | # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 137 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release 138 | - name: Archive Artifacts 139 | uses: actions/upload-artifact@master 140 | with: 141 | name: remoting-linux64 142 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/linux64 143 | retention-days: 1 144 | container-windows64: 145 | runs-on: windows-latest 146 | steps: 147 | - uses: actions/checkout@v4 148 | 149 | - name: Set reusable strings 150 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 151 | id: strings 152 | shell: bash 153 | run: | 154 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 155 | 156 | - name: Configure CMake 157 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 158 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 159 | run: > 160 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 161 | -DCMAKE_CXX_COMPILER=cl 162 | -DCMAKE_C_COMPILER=cl 163 | -DCMAKE_BUILD_TYPE=Release 164 | -A x64 165 | -S ${{ github.workspace }}/container 166 | 167 | - name: Build 168 | # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 169 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release 170 | - name: Archive Artifacts 171 | uses: actions/upload-artifact@master 172 | with: 173 | name: container-win64 174 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/win64 175 | retention-days: 1 176 | container-linux64: 177 | runs-on: ubuntu-latest 178 | steps: 179 | - uses: actions/checkout@v4 180 | 181 | - name: Set reusable strings 182 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 183 | id: strings 184 | shell: bash 185 | run: | 186 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 187 | 188 | - name: Configure CMake 189 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 190 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 191 | run: > 192 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 193 | -DCMAKE_CXX_COMPILER=g++ 194 | -DCMAKE_C_COMPILER=gcc 195 | -DCMAKE_BUILD_TYPE=Release 196 | -S ${{ github.workspace }}/container 197 | 198 | - name: Build 199 | # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 200 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release 201 | - name: Archive Artifacts 202 | uses: actions/upload-artifact@master 203 | with: 204 | name: container-linux64 205 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/linux64 206 | retention-days: 1 207 | package: 208 | needs: [remoting-windows32, remoting-windows64, remoting-linux32, remoting-linux64, container-windows64, container-linux64] 209 | runs-on: ubuntu-latest 210 | steps: 211 | - uses: actions/checkout@v4 212 | - uses: actions/download-artifact@master 213 | with: 214 | name: remoting-win64 215 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/win64/ 216 | - uses: actions/download-artifact@master 217 | with: 218 | name: remoting-win32 219 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/win32/ 220 | - uses: actions/download-artifact@master 221 | with: 222 | name: remoting-linux32 223 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/linux32/ 224 | - uses: actions/download-artifact@master 225 | with: 226 | name: remoting-linux64 227 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/linux64/ 228 | - uses: actions/download-artifact@master 229 | with: 230 | name: container-win64 231 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/win64/ 232 | - uses: actions/download-artifact@master 233 | with: 234 | name: container-linux64 235 | path: ${{ github.workspace }}/fmu_manipulation_toolbox/resources/linux64/ 236 | - name: Set up Python 3.10 237 | uses: actions/setup-python@v3 238 | with: 239 | python-version: "3.10" 240 | - name: Install dependencies 241 | run: | 242 | python -m pip install --upgrade pip 243 | pip install build 244 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 245 | - name: Test with pytest 246 | run: | 247 | cd tests 248 | python -munittest -v test_suite.py 249 | - name: Package 250 | run: | 251 | python -m build 252 | - name: Archive Artifacts 253 | uses: actions/upload-artifact@master 254 | with: 255 | name: package 256 | path: ${{ github.workspace }}/dist 257 | retention-days: 1 258 | publish: 259 | if: startsWith(github.ref, 'refs/tags/V') 260 | needs: [package] 261 | runs-on: ubuntu-latest 262 | steps: 263 | - uses: actions/checkout@v4 264 | - uses: actions/download-artifact@master 265 | with: 266 | name: package 267 | path: ${{ github.workspace }}/dist 268 | - name: Publish package 269 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 270 | with: 271 | user: __token__ 272 | password: ${{ secrets.PYPI_API_TOKEN }} 273 | - name: Publish package distributions to PyPI 274 | uses: pypa/gh-action-pypi-publish@release/v1 275 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | __pycache__ 3 | .DS_Store 4 | .vs 5 | *.dll 6 | *.exe 7 | tests/operations/bouncing_ball-keeponly.csv 8 | tests/operations/bouncing_ball-keeponly.fmu 9 | tests/operations/bouncing_ball-no-tl.csv 10 | tests/operations/bouncing_ball-no-tl.fmu 11 | tests/operations/bouncing_ball-removed.csv 12 | tests/operations/bouncing_ball-removed.fmu 13 | tests/operations/bouncing_ball-renamed.csv 14 | tests/operations/bouncing_ball-renamed.fmu 15 | tests/bouncing_ball-win32.fmu 16 | remoting/out 17 | remoting/build 18 | dist 19 | tests/containers/bouncing_ball/bouncing 20 | tests/containers/bouncing_ball/bouncing.fmu 21 | tests/containers/bouncing_ball/bouncing_auto.fmu 22 | tests/containers/bouncing_ball/bouncing_unlinked.fmu 23 | container/build-win64 24 | container/build 25 | tests/operations/bouncing_ball.csv 26 | fmu_manipulation_toolbox/resources/darwin64/client_sm.dylib 27 | fmu_manipulation_toolbox/resources/darwin64/container.dylib 28 | fmu_manipulation_toolbox/resources/darwin64/server_sm 29 | tests/containers/bouncing_ball/bouncing.json 30 | tests/containers/arch/hierarchical.fmu 31 | tests/containers/arch/flat.fmu 32 | tests/containers/arch/reversed.fmu 33 | tests/containers/ssp/bouncing.fmu 34 | tests/containers/ssp/bouncing 35 | tests/containers/ssp/bouncing-dump.json 36 | tests/containers/arch/flat-dump.json 37 | tests/containers/arch/hierarchical-dump.json 38 | tests/containers/arch/reversed-dump.json 39 | tests/containers/arch/sub.fmu 40 | /tests/containers/bouncing_ball/bouncing-profiling/ 41 | tests/containers/bouncing_ball/bouncing-profiling.fmu 42 | tests/containers/bouncing_ball/bouncing-profiling.json 43 | tests/containers/start/container-slx 44 | tests/containers/start/container-slx.fmu 45 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # FMU Manipulation Toolbox changelog 2 | This package was formerly known as `fmutool`. 3 | 4 | # Version 1.8.4.2 5 | * FIXED: `fmucontainer` re-set start values for causality=input after fmi2EnterInitialization as workaround for some FMUs. 6 | 7 | # Version 1.8.4.1 8 | * FIXED: `fmucontainer` store embedded FMUs into shorter named directories to avoid windows-long-path-issue 9 | * FIXED: `fmucontainer` can refer to FMU located in subdirectory 10 | * FIXED: `fmucontainer` profiling reported incorrect values 11 | 12 | # Version 1.8.4 13 | * CHANGE: `fmucontainer` option `-dump` saves explicitly auto-wiring data. Auto-wiring is off for SSP format. 14 | 15 | # Version 1.8.3 16 | * FIXED: `fmucontainer` supports correctly auto-link for 1-to-n links 17 | 18 | # Version 1.8.2.2 19 | * ADDED: `fmucontainer` support fmi2Strings 20 | * FIXED: `fmucontainer` freeInstance correctly frees memory 21 | 22 | ## Version 1.8.2.1 23 | * FIXED: `fmucontainer` exposed parameters name (using fmu name instead of fmu's model identifier) 24 | * FIXED: `fmucontainer` support for long path (on Windows) 25 | 26 | ## Version 1.8.2 27 | * FIXED: `fmucontainer` identifier (for coSimulation) does not contain ".fmu" anymore 28 | * ADDED: `fmucontainer` log more information when embedded FMU cannot be loaded 29 | * ADDED: `fmucontainer` startTime and stopTime are deduced from the 1st embedded FMU 30 | * ADDED: `fmucontainer` support `-auto-local` option to expose local variables of the embedded FMU 31 | * ADDED: `fmucontainer` support new option `-auto-parameter` (parameters are not exposed by default) 32 | * FIXED: `fmutool` support "apply on" filter correctly 33 | * DEV: preliminary version of GUI for `fmucontainer` 34 | 35 | ## Version 1.8.1 36 | * FIXED: `fmucontainer` read links from `.json` input files 37 | * CHANGE: switch to PyQT6 and add minor GUI improvements 38 | 39 | ## Version 1.8 40 | * CHANGE: Python Package in now known as `fmu-manipulation-toolbox` 41 | * ADDED: `fmucontainer` support `canHandleVariableCommunicationStepSize` 42 | * ADDED: `fmucontainer` support `.ssp` or `.json` as input files 43 | * ADDED: `fmucontainer` new `-dump` option to save container description 44 | 45 | ## Version 1.7.4 46 | * ADDED: `fmucontainer` Linux support 47 | * ADDED: `-fmu-directory` option defaults to "." if not set 48 | * FIXED: `fmucontainer` ensures that FMUs are compliant with version 2.0 of FMU Standard 49 | * FIXED: `fmucontainer` handles the lack of DefaultExperiment section in modelDescription.xml 50 | 51 | 52 | ## Version 1.7.3 53 | * ADDED: `fmucontainer` supports `-profile` option to expose RT ratio of embedded FMUs during simulation 54 | * ADDED: Ability to expose local variables of embedded FMUs at container level 55 | * FIXED: `fmucontainer` handles missing causality and handle better variability 56 | 57 | ## Version 1.7.2 58 | * FIXED: handle `` section for `fmucontainer` 59 | * FIXED: brought back `-h` option to get help for `fmucontainer` 60 | * CHANGED: make compatibility with python >= 3.9 61 | 62 | ## Version 1.7.1 63 | * FIXED: add missing *.xsd file that prevented checker to work 64 | * CHANGED: Checker use API instead of environment variable to declare custom checkers. 65 | 66 | ## Version 1.7 67 | * ADDED: FMUContainer tool 68 | 69 | ## Version 1.6.2 70 | * ADDED: Ability to add your own FMU Checker. 71 | * ADDED: SaveNamesToCSV will dump scalar types and start values. 72 | * CHANGED: Default (Generic) Checker checks the `modelDescription.xml` conformity against the XSD specification file. 73 | 74 | ## Version 1.6.1 75 | * FIXED: publication workflow is fully automated. 76 | * FIXED: `fmutool` script is now part of the distribution. 77 | * CHANGED: minor enhancement in the README file 78 | 79 | ## Version 1.6 80 | * First public release 81 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Renault SAS - Nicolas.LAURENT@Renault.com 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 1. Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 | SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /container/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Nicolas.LAURENT@Renault.com 3 | # 4 | # To build for 64bits, use `cmake -A x64` and `cmake --config Release` 5 | # To build for 32bits, use `cmake -A x86` and `cmake --config Release` 6 | # 7 | 8 | cmake_minimum_required(VERSION 3.20) 9 | project(container C) 10 | set (CMAKE_C_STANDARD 99) 11 | 12 | 13 | if (WIN32) 14 | if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") 15 | set(FMI_PLATFORM win64) 16 | else () 17 | set(FMI_PLATFORM win32) 18 | endif () 19 | else () 20 | set(THREADS_PREFER_PTHREAD_FLAG ON) 21 | find_package(Threads REQUIRED) 22 | 23 | if (APPLE) 24 | set(FMI_PLATFORM darwin64) 25 | else () 26 | set(FMI_PLATFORM linux64) 27 | endif() 28 | endif () 29 | message("FMI_PLATFORM: ${FMI_PLATFORM}") 30 | 31 | #################### 32 | # Create CONTAINER # 33 | #################### 34 | add_library(container SHARED 35 | container.c container.h 36 | fmu.c fmu.h 37 | hash.c hash.h 38 | library.c library.h 39 | logger.c logger.h 40 | profile.c profile.h 41 | thread.c thread.h) 42 | set_target_properties(container PROPERTIES PREFIX "") 43 | target_include_directories(container PRIVATE 44 | ${CMAKE_CURRENT_SOURCE_DIR}/../fmi 45 | ) 46 | if (UNIX AND NOT APPLE) 47 | target_link_libraries(container Threads::Threads) 48 | endif() 49 | if (WIN32) 50 | target_link_libraries(container Imagehlp.lib) 51 | endif() 52 | set_target_properties(container PROPERTIES 53 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../fmu_manipulation_toolbox/resources/${FMI_PLATFORM}" 54 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../fmu_manipulation_toolbox/resources/${FMI_PLATFORM}") 55 | 56 | #target_compile_options(container PRIVATE /W4 /WX) 57 | 58 | -------------------------------------------------------------------------------- /container/README.md: -------------------------------------------------------------------------------- 1 | # FMU Containers ? 2 | 3 | An FMU Container is classical FMU which embeds other's FMU's. See [FMU Container](../doc/container.md) 4 | for user documentation. 5 | 6 | From API point of view, an FMUContainer can be seen as 7 | * an FMU: see [](container.c) which implements the FMI API 8 | * an fmi-importer which can load (see [`library.c`](library.c)) and interact with FMUs (see [`fmu.c`](fmu.c)) 9 | 10 | A configuration file `resource/container.txt` describe 11 | * the container configuration (inputs, outputs, time step, options) 12 | * the routing table which defines how the embedded FMUs are connected together 13 | 14 | This file is generated by the `fmucontainer` command line tool. 15 | See [`fmu_container.py`](../fmu_manipulation_toolbox/fmu_container.py) for details on how to create 16 | a container. 17 | 18 | # Muti-Thread 19 | If enabled through `MT` flag, each FMU has its own thread which 20 | 1. fetch its inputs from container buffer which is shared with all FMUs. 21 | 2. process `fmi2DoStep()` 22 | 23 | Synchronization uses [`thread.c`](thread.c) facilities. 24 | 25 | # Profiling 26 | If enabled through `profiling` flag, each call to `fmi2DoStep` of each FMU is monitored. The elapsed time 27 | is compared with `currentCommunicationPoint` and a RT ratio is computed. A ratio greater than `1.0` means 28 | this particular FMU is faster than RT. (See [`profile.c`](profile.c) for details) 29 | -------------------------------------------------------------------------------- /container/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | Rem Local build script. Used for debugging purpose. 3 | echo *** 4 | echo *** Compilation 64bits 5 | echo *** 6 | mkdir build-win64 7 | cd build-win64 8 | cmake .. -A x64 9 | cmake --build . --config Release 10 | cd .. 11 | rmdir /s /q build-win64 12 | 13 | 14 | echo *** 15 | echo *** DONE 16 | echo *** -------------------------------------------------------------------------------- /container/container.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTAINER_H 2 | #define CONTAINER_H 3 | 4 | # ifdef __cplusplus 5 | extern "C" { 6 | # endif 7 | 8 | #include "fmu.h" 9 | #include "library.h" 10 | 11 | /*---------------------------------------------------------------------------- 12 | C O N T A I N E R _ V R _ T 13 | ----------------------------------------------------------------------------*/ 14 | typedef struct { 15 | fmi2ValueReference fmu_vr; 16 | int fmu_id; 17 | } container_vr_t; 18 | 19 | 20 | /*---------------------------------------------------------------------------- 21 | C O N T A I N E R _ P O R T _ T 22 | ----------------------------------------------------------------------------*/ 23 | typedef struct { 24 | int nb; /* number of connected FMU from a container port */ 25 | container_vr_t *links; 26 | } container_port_t; 27 | 28 | 29 | /*---------------------------------------------------------------------------- 30 | C O N T A I N E R _ T 31 | ----------------------------------------------------------------------------*/ 32 | typedef struct container_s { 33 | int mt; 34 | int profiling; 35 | int nb_fmu; 36 | fmu_t *fmu; 37 | fmi2CallbackLogger logger; 38 | fmi2ComponentEnvironment environment; 39 | char *instance_name; 40 | char *uuid; 41 | fmi2Boolean debug; 42 | const fmi2CallbackFunctions *callback_functions; 43 | 44 | /* storage of local variables (conveyed from one FMU to an other) */ 45 | fmi2ValueReference nb_local_reals; 46 | fmi2ValueReference nb_local_integers; 47 | fmi2ValueReference nb_local_booleans; 48 | fmi2ValueReference nb_local_strings; 49 | fmi2Real *reals; 50 | fmi2Integer *integers; 51 | fmi2Boolean *booleans; 52 | fmi2String *strings; 53 | 54 | /* container ports definition */ 55 | #define DECLARE_PORT(type) \ 56 | fmi2ValueReference nb_ports_ ## type; \ 57 | container_vr_t *vr_ ## type; /* used as buffer to optimize malloc() operations */ \ 58 | container_port_t *port_ ## type 59 | 60 | DECLARE_PORT(reals); 61 | DECLARE_PORT(integers); 62 | DECLARE_PORT(booleans); 63 | DECLARE_PORT(strings); 64 | #undef DECLARE_PORT 65 | 66 | /* for doStep() operations */ 67 | fmi2Real time_step; 68 | fmi2Real time; 69 | fmi2Real tolerance; 70 | fmi2Boolean noSetFMUStatePriorToCurrentPoint; 71 | 72 | } container_t; 73 | 74 | 75 | /* 76 | * No prototypes explicitly exposed here: this file implemenets FMI2 API ! 77 | */ 78 | 79 | # ifdef __cplusplus 80 | } 81 | # endif 82 | #endif 83 | -------------------------------------------------------------------------------- /container/fmu.h: -------------------------------------------------------------------------------- 1 | #ifndef FMU_H 2 | # define FMU_H 3 | 4 | # ifdef __cplusplus 5 | extern "C" { 6 | # endif 7 | 8 | # include "fmi2Functions.h" 9 | # include "container.h" 10 | # include "library.h" 11 | # include "profile.h" 12 | # include "thread.h" 13 | 14 | 15 | 16 | /*---------------------------------------------------------------------------- 17 | F M U _ T R A N S L A T I O N _ T 18 | ----------------------------------------------------------------------------*/ 19 | typedef struct { 20 | fmi2ValueReference vr; 21 | fmi2ValueReference fmu_vr; 22 | } fmu_translation_t; 23 | 24 | 25 | /*---------------------------------------------------------------------------- 26 | F M U _ T R A N S L A T I O N _ L I S T _ T 27 | ----------------------------------------------------------------------------*/ 28 | typedef struct { 29 | fmi2ValueReference nb; 30 | fmu_translation_t *translations; 31 | } fmu_translation_list_t; 32 | 33 | 34 | /*---------------------------------------------------------------------------- 35 | F M U _ T R A N S L A T I O N _ P O R T _ T 36 | ----------------------------------------------------------------------------*/ 37 | typedef struct { 38 | fmu_translation_list_t in; 39 | fmu_translation_list_t out; 40 | } fmu_translation_port_t; 41 | 42 | 43 | /*---------------------------------------------------------------------------- 44 | F M U _ S T A R T _ xxx _ T 45 | ----------------------------------------------------------------------------*/ 46 | 47 | #define DECLARE_START_TYPE(name, type) \ 48 | typedef struct { \ 49 | fmi2ValueReference nb; \ 50 | struct { \ 51 | fmi2ValueReference vr; \ 52 | int reset; \ 53 | type value; \ 54 | } *start_values; \ 55 | } fmu_start_ ## name ## _t 56 | 57 | DECLARE_START_TYPE(reals, fmi2Real); 58 | DECLARE_START_TYPE(integers, fmi2Integer); 59 | DECLARE_START_TYPE(booleans, fmi2Boolean); 60 | DECLARE_START_TYPE(strings, fmi2String); 61 | 62 | #undef DECLARE_TYPE_START 63 | 64 | 65 | /*---------------------------------------------------------------------------- 66 | F M U _ I O _ T 67 | ----------------------------------------------------------------------------*/ 68 | typedef struct { 69 | fmu_translation_port_t reals; 70 | fmu_translation_port_t integers; 71 | fmu_translation_port_t booleans; 72 | fmu_translation_port_t strings; 73 | 74 | fmu_start_reals_t start_reals; 75 | fmu_start_integers_t start_integers; 76 | fmu_start_booleans_t start_booleans; 77 | fmu_start_strings_t start_strings; 78 | } fmu_io_t; 79 | 80 | 81 | /*---------------------------------------------------------------------------- 82 | F M U _ I N T E R F A C E _ T 83 | ----------------------------------------------------------------------------*/ 84 | typedef struct { 85 | # define DECLARE_FMI_FUNCTION(x) x ## TYPE *x 86 | DECLARE_FMI_FUNCTION(fmi2GetTypesPlatform); 87 | DECLARE_FMI_FUNCTION(fmi2GetVersion); 88 | DECLARE_FMI_FUNCTION(fmi2SetDebugLogging); 89 | DECLARE_FMI_FUNCTION(fmi2Instantiate); 90 | DECLARE_FMI_FUNCTION(fmi2FreeInstance); 91 | DECLARE_FMI_FUNCTION(fmi2SetupExperiment); 92 | DECLARE_FMI_FUNCTION(fmi2EnterInitializationMode); 93 | DECLARE_FMI_FUNCTION(fmi2ExitInitializationMode); 94 | DECLARE_FMI_FUNCTION(fmi2Terminate); 95 | DECLARE_FMI_FUNCTION(fmi2Reset); 96 | DECLARE_FMI_FUNCTION(fmi2GetReal); 97 | DECLARE_FMI_FUNCTION(fmi2GetInteger); 98 | DECLARE_FMI_FUNCTION(fmi2GetBoolean); 99 | DECLARE_FMI_FUNCTION(fmi2GetString); 100 | DECLARE_FMI_FUNCTION(fmi2SetReal); 101 | DECLARE_FMI_FUNCTION(fmi2SetInteger); 102 | DECLARE_FMI_FUNCTION(fmi2SetBoolean); 103 | DECLARE_FMI_FUNCTION(fmi2SetString); 104 | DECLARE_FMI_FUNCTION(fmi2GetFMUstate); 105 | DECLARE_FMI_FUNCTION(fmi2SetFMUstate); 106 | DECLARE_FMI_FUNCTION(fmi2FreeFMUstate); 107 | DECLARE_FMI_FUNCTION(fmi2SerializedFMUstateSize); 108 | DECLARE_FMI_FUNCTION(fmi2SerializeFMUstate); 109 | DECLARE_FMI_FUNCTION(fmi2DeSerializeFMUstate); 110 | DECLARE_FMI_FUNCTION(fmi2GetDirectionalDerivative); 111 | DECLARE_FMI_FUNCTION(fmi2SetRealInputDerivatives); 112 | DECLARE_FMI_FUNCTION(fmi2GetRealOutputDerivatives); 113 | DECLARE_FMI_FUNCTION(fmi2DoStep); 114 | DECLARE_FMI_FUNCTION(fmi2CancelStep); 115 | DECLARE_FMI_FUNCTION(fmi2GetStatus); 116 | DECLARE_FMI_FUNCTION(fmi2GetRealStatus); 117 | DECLARE_FMI_FUNCTION(fmi2GetIntegerStatus); 118 | DECLARE_FMI_FUNCTION(fmi2GetBooleanStatus); 119 | DECLARE_FMI_FUNCTION(fmi2GetStringStatus); 120 | } fmu_interface_t; 121 | # undef DECLARE_FMI_FUNCTION 122 | 123 | /*---------------------------------------------------------------------------- 124 | F M U _ T 125 | ----------------------------------------------------------------------------*/ 126 | # define FMU_PATH_MAX_LEN 4096 127 | 128 | typedef struct { 129 | char *name; /* based on directory */ 130 | int index; /* index of this FMU in container */ 131 | library_t library; 132 | char resource_dir[FMU_PATH_MAX_LEN]; 133 | char *guid; 134 | fmi2Component component; 135 | 136 | fmi2CallbackFunctions fmi_callback_functions; 137 | fmu_interface_t fmi_functions; 138 | 139 | thread_t thread; 140 | mutex_t mutex_fmu; 141 | mutex_t mutex_container; 142 | 143 | fmu_io_t fmu_io; 144 | 145 | fmi2Status status; 146 | int cancel; 147 | int set_input; 148 | 149 | profile_t *profile; 150 | 151 | struct container_s *container; 152 | } fmu_t; 153 | 154 | 155 | /*---------------------------------------------------------------------------- 156 | P R O T O T Y P E S 157 | ----------------------------------------------------------------------------*/ 158 | 159 | extern fmi2Status fmu_set_inputs(fmu_t *fmu); 160 | extern int fmu_load_from_directory(struct container_s *container, int i, 161 | const char *directory, const char *name, 162 | const char *identifier, const char *guid); 163 | extern void fmu_unload(fmu_t *fmu); 164 | 165 | extern fmi2Status fmuGetReal(const fmu_t *fmu, const fmi2ValueReference vr[], 166 | size_t nvr, fmi2Real value[]); 167 | extern fmi2Status fmuGetInteger(const fmu_t *fmu, const fmi2ValueReference vr[], 168 | size_t nvr, fmi2Integer value[]); 169 | extern fmi2Status fmuGetBoolean(const fmu_t *fmu, const fmi2ValueReference vr[], 170 | size_t nvr, fmi2Boolean value[]); 171 | extern fmi2Status fmuGetString(const fmu_t* fmu, const fmi2ValueReference vr[], 172 | size_t nvr, fmi2String value[]); 173 | extern fmi2Status fmuSetReal(const fmu_t *fmu, const fmi2ValueReference vr[], 174 | size_t nvr, const fmi2Real value[]); 175 | extern fmi2Status fmuSetInteger(const fmu_t *fmu, const fmi2ValueReference vr[], 176 | size_t nvr, const fmi2Integer value[]); 177 | extern fmi2Status fmuSetBoolean(const fmu_t *fmu, const fmi2ValueReference vr[], 178 | size_t nvr, const fmi2Boolean value[]); 179 | extern fmi2Status fmuSetString(const fmu_t* fmu, const fmi2ValueReference vr[], 180 | size_t nvr, const fmi2String value[]); 181 | extern fmi2Status fmuDoStep(const fmu_t *fmu, 182 | fmi2Real currentCommunicationPoint, 183 | fmi2Real communicationStepSize, 184 | fmi2Boolean noSetFMUStatePriorToCurrentPoint); 185 | extern fmi2Status fmuEnterInitializationMode(const fmu_t *fmu); 186 | extern fmi2Status fmuExitInitializationMode(const fmu_t *fmu); 187 | extern fmi2Status fmuSetupExperiment(const fmu_t *fmu, 188 | fmi2Boolean toleranceDefined, 189 | fmi2Real tolerance, 190 | fmi2Real startTime, 191 | fmi2Boolean stopTimeDefined, 192 | fmi2Real stopTime); 193 | extern fmi2Status fmuInstantiate(fmu_t *fmu, 194 | fmi2String instanceName, 195 | fmi2Type fmuType, 196 | fmi2Boolean visible, 197 | fmi2Boolean loggingOn); 198 | extern void fmuFreeInstance(const fmu_t *fmu); 199 | extern fmi2Status fmuTerminate(const fmu_t *fmu); 200 | extern fmi2Status fmuReset(const fmu_t *fmu); 201 | extern fmi2Status fmi2GetBooleanStatus(fmi2Component c, const fmi2StatusKind s, fmi2Boolean* value); 202 | extern fmi2Status fmuGetRealStatus(const fmu_t *fmu, const fmi2StatusKind s, fmi2Real* value); 203 | extern fmi2Status fmuGetBooleanStatus(const fmu_t *fmu, const fmi2StatusKind s, fmi2Boolean* value); 204 | 205 | # ifdef __cplusplus 206 | } 207 | # endif 208 | #endif 209 | -------------------------------------------------------------------------------- /container/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "hash.h" 5 | 6 | 7 | #define HASH_DEFAULT_SIZE 201 /* prime number far from power of 2 numbers */ 8 | #define HASH_COLLISION_MAX 100 9 | 10 | 11 | static unsigned long hash_function(const char *key, unsigned long hash_size) { 12 | register unsigned long int val=0; 13 | 14 | while(*key != '\0') 15 | { 16 | register unsigned long int tmp; 17 | val = (val << 4) + (*key); 18 | tmp = val & 0xf0000000; 19 | if (tmp) 20 | { 21 | val ^= tmp >> 24; 22 | val ^= tmp; 23 | } 24 | 25 | key++; 26 | } 27 | 28 | return(val % hash_size); 29 | } 30 | 31 | 32 | hash_entry_t* hash_get_entry(const hash_t* hash, const char* key) { 33 | unsigned long index = hash_function(key, hash->size); 34 | 35 | for (hash_entry_t* entry = hash->entry[index]; entry; entry = entry->next) 36 | if (strcmp(entry->key, key) == 0) 37 | return entry; 38 | 39 | return NULL; 40 | } 41 | 42 | 43 | void *hash_get_value(const hash_t *hash, const char *key) { 44 | hash_entry_t *entry = hash_get_entry(hash, key); 45 | if (entry) 46 | return entry->value; 47 | 48 | return NULL; 49 | } 50 | 51 | 52 | static hash_entry_t *hash_entry_new(char *key, void *value) { 53 | hash_entry_t *entry; 54 | 55 | entry = malloc(sizeof(*entry)); 56 | entry->key = key; 57 | entry->value = value; 58 | entry->next = NULL; 59 | 60 | return entry; 61 | } 62 | 63 | 64 | static void hash_entry_free(hash_entry_t *entry) { 65 | free(entry->key); 66 | free(entry->value); 67 | free(entry); 68 | } 69 | 70 | 71 | unsigned long hash_entries_number(const hash_t *hash) { 72 | return hash->nb; 73 | } 74 | 75 | 76 | static void hash_reorder(hash_t *hash) 77 | { 78 | unsigned long i; 79 | const unsigned long N = hash->size; 80 | const unsigned long N2 = N * 2 + 1; /* pour avoir un nombre impaire */ 81 | hash_entry_t **new_entry; 82 | 83 | /* Initialisation de la nouvelle table */ 84 | new_entry = malloc(sizeof(*new_entry) * N2); 85 | for (i = 0; ientry[i]; e; e = next) 95 | { 96 | unsigned long indice; 97 | next = e->next; 98 | 99 | indice = hash_function(e->key, N2); 100 | e->next = new_entry[indice]; 101 | new_entry[indice] = e; 102 | } 103 | } 104 | 105 | /* lib�ration de l'ancienne et M-�-J */ 106 | free(hash->entry); 107 | hash->entry = new_entry; 108 | hash->size = N2; 109 | 110 | return; 111 | } 112 | 113 | 114 | void hash_set_value(hash_t *hash, char *key, void *value) { 115 | hash_entry_t *entry = hash_get_entry(hash, key); 116 | if (entry) { 117 | free(entry->value); 118 | entry->value = value; 119 | } else { 120 | unsigned collision = 0; 121 | unsigned long index = hash_function(key, hash->size); 122 | hash_entry_t *current = hash->entry[index]; 123 | hash->entry[index] = hash_entry_new(key, value); 124 | hash->entry[index]->next = current; 125 | hash->nb += 1; 126 | 127 | /* calculate collision */ 128 | for (entry = hash->entry[index]; entry->next; entry = entry->next) 129 | collision += 1; 130 | if (collision >= HASH_COLLISION_MAX) 131 | hash_reorder(hash); 132 | } 133 | } 134 | 135 | 136 | hash_t *hash_new(void) { 137 | hash_t *hash; 138 | 139 | hash = malloc(sizeof(*hash)); 140 | hash->size = HASH_DEFAULT_SIZE; 141 | hash->nb = 0; 142 | hash->entry = malloc(sizeof(*hash->entry) * hash->size); 143 | for(unsigned long i = 0; i < hash->size; i += 1) 144 | hash->entry[i] = NULL; 145 | 146 | return hash; 147 | } 148 | 149 | 150 | void hash_free(hash_t *hash) { 151 | for(unsigned long i = 0; i < hash->size; i += 1) { 152 | hash_entry_t *entry = hash->entry[i]; 153 | while(entry) { 154 | hash_entry_t *next = entry->next; 155 | hash_entry_free(entry); 156 | entry =next; 157 | } 158 | } 159 | free(hash->entry); 160 | free(hash); 161 | } 162 | -------------------------------------------------------------------------------- /container/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_T 2 | #define HASH_T 3 | 4 | # ifdef __cplusplus 5 | extern "C" { 6 | # endif 7 | 8 | 9 | /*---------------------------------------------------------------------------- 10 | H A S H _ E N T R Y _ T 11 | ----------------------------------------------------------------------------*/ 12 | 13 | typedef struct hash_entry_s { 14 | char *key; 15 | void *value; 16 | struct hash_entry_s *next; 17 | } hash_entry_t; 18 | 19 | 20 | /*---------------------------------------------------------------------------- 21 | H A S H _ T 22 | ----------------------------------------------------------------------------*/ 23 | 24 | typedef struct { 25 | unsigned long size; 26 | unsigned long nb; 27 | hash_entry_t **entry; 28 | } hash_t; 29 | 30 | 31 | /*---------------------------------------------------------------------------- 32 | H A S H _ LOOP _ F U N C T I O N _ T 33 | ----------------------------------------------------------------------------*/ 34 | 35 | typedef void (*hash_loop_function_t)(const hash_entry_t *, void *data); 36 | 37 | 38 | /*---------------------------------------------------------------------------- 39 | P R O T O T Y P E S 40 | ----------------------------------------------------------------------------*/ 41 | 42 | extern void hash_free(hash_t *hash); 43 | extern hash_t *hash_new(void); 44 | extern unsigned long hash_entries_number(const hash_t *hash); 45 | extern void hash_set_value(hash_t *hash, char *key, void *value); 46 | extern void *hash_get_value(const hash_t *hash, const char *key); 47 | extern hash_entry_t *hash_get_entry(const hash_t* hash, const char* key); 48 | extern hash_entry_t* hash_get_entry_closest(const hash_t* hash, const char* key); 49 | extern void hash_loop(const hash_t* hash, hash_loop_function_t function, void *data); 50 | 51 | # ifdef __cplusplus 52 | } 53 | # endif 54 | #endif 55 | -------------------------------------------------------------------------------- /container/library.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 2022-2024 Nicolas.LAURENT@Renault.com 3 | * 4 | * DLL/SO loading functions. 5 | */ 6 | 7 | #ifdef WIN32 8 | # include 9 | # include 10 | #else 11 | # include 12 | # include 13 | #endif 14 | 15 | #include "hash.h" 16 | #include "library.h" 17 | #include "logger.h" 18 | 19 | 20 | #ifdef WIN32 21 | typedef enum { 22 | LIBRARY_DLL_NOT_FOUND, 23 | LIBRARY_DLL_MISSING_DEPENDENCIES, 24 | LIBRARY_DLL_OK 25 | } libray_status_t; 26 | 27 | static libray_status_t* libray_analyse(hash_t* dll_db, const char* filename); 28 | #endif 29 | 30 | void *library_symbol(library_t library, const char *symbol_name) { 31 | #ifdef WIN32 32 | return (void *)GetProcAddress(library, symbol_name); 33 | #else 34 | return dlsym(library, symbol_name); 35 | #endif 36 | } 37 | 38 | 39 | static void library_load_error(const char* library_filename) { 40 | #ifdef WIN32 41 | hash_t* dll_db = hash_new(); 42 | libray_analyse(dll_db, library_filename); 43 | hash_free(dll_db); 44 | #else 45 | logger(fmi2Error, "dlopen Error: %s", dlerror()); 46 | #endif 47 | logger(fmi2Error, "Cannot load `%s'", library_filename); 48 | 49 | return; /* Never reached */ 50 | } 51 | 52 | 53 | library_t library_load(const char* library_filename) { 54 | library_t handle; 55 | #ifdef WIN32 56 | handle = LoadLibraryA(library_filename); 57 | #else 58 | handle = dlopen(library_filename, RTLD_LAZY); /* RTLD_LOCAL can lead to failure */ 59 | #endif 60 | if (!handle) 61 | library_load_error(library_filename); 62 | 63 | return handle; 64 | } 65 | 66 | 67 | void library_unload(library_t library) { 68 | if (library) { 69 | #ifdef WIN32 70 | FreeLibrary(library); 71 | #else 72 | dlclose(library); 73 | #endif 74 | } 75 | } 76 | 77 | /*---------------------------------------------------------------------------- 78 | D E P E N D E N C I E S A N A L Y S I S 79 | ----------------------------------------------------------------------------*/ 80 | /* 81 | * see: https://stackoverflow.com/questions/597260/how-to-determine-a-windows-executables-dll-dependencies-programmatically 82 | */ 83 | #ifdef WIN32 84 | static PIMAGE_SECTION_HEADER GetEnclosingSectionHeader(DWORD rva, PIMAGE_NT_HEADERS pNTHeader) { 85 | PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader); 86 | unsigned i; 87 | 88 | for (i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++) 89 | { 90 | // This 3 line idiocy is because Watcom's linker actually sets the 91 | // Misc.VirtualSize field to 0. (!!! - Retards....!!!) 92 | DWORD size = section->Misc.VirtualSize; 93 | if (0 == size) 94 | size = section->SizeOfRawData; 95 | 96 | // Is the RVA within this section? 97 | if ((rva >= section->VirtualAddress) && 98 | (rva < (section->VirtualAddress + size))) 99 | return section; 100 | } 101 | 102 | return 0; 103 | } 104 | 105 | 106 | static LPVOID GetPtrFromRVA(DWORD rva, PIMAGE_NT_HEADERS pNTHeader, PBYTE imageBase) { 107 | PIMAGE_SECTION_HEADER pSectionHdr; 108 | INT delta; 109 | 110 | pSectionHdr = GetEnclosingSectionHeader(rva, pNTHeader); 111 | if (!pSectionHdr) 112 | return 0; 113 | 114 | delta = (INT)(pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData); 115 | return (PVOID)(imageBase + rva - delta); 116 | } 117 | 118 | 119 | static libray_status_t* libray_try_load(const char* filename) { 120 | libray_status_t* status = malloc(sizeof(*status)); 121 | 122 | HINSTANCE handle = LoadLibraryExA(filename, NULL, DONT_RESOLVE_DLL_REFERENCES); 123 | if (handle) { 124 | FreeLibrary(handle); 125 | 126 | handle = LoadLibraryA(filename); 127 | if (handle) { 128 | FreeLibrary(handle); 129 | *status = LIBRARY_DLL_OK; 130 | } 131 | else { 132 | *status = LIBRARY_DLL_MISSING_DEPENDENCIES; 133 | } 134 | } 135 | else { 136 | *status = LIBRARY_DLL_NOT_FOUND; 137 | } 138 | return status; 139 | } 140 | 141 | 142 | static void libray_log_status(const char* filename, libray_status_t* status) { 143 | switch (*status) { 144 | case LIBRARY_DLL_OK: 145 | logger(fmi2Warning, "DLL `%s' is found.", filename); 146 | break; 147 | case LIBRARY_DLL_NOT_FOUND: 148 | logger(fmi2Warning, "DLL `%s' is not found.", filename); 149 | break; 150 | case LIBRARY_DLL_MISSING_DEPENDENCIES: 151 | logger(fmi2Warning, "DLL `%s' has missing dependencies.", filename); 152 | break; 153 | } 154 | } 155 | 156 | 157 | static void libray_analyse_deeply(hash_t* dll_db, const char* filename) { 158 | PLOADED_IMAGE image = ImageLoad(filename, NULL); 159 | if (!image) { 160 | logger(fmi2Error, "Cannot ImageLoad(%s)", filename); 161 | } 162 | else { 163 | if (image->FileHeader->OptionalHeader.NumberOfRvaAndSizes >= 2) { 164 | PIMAGE_IMPORT_DESCRIPTOR importDesc = 165 | (PIMAGE_IMPORT_DESCRIPTOR)GetPtrFromRVA( 166 | image->FileHeader->OptionalHeader.DataDirectory[1].VirtualAddress, 167 | image->FileHeader, image->MappedAddress); 168 | while (1) { 169 | // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR 170 | if ((importDesc->TimeDateStamp == 0) && (importDesc->Name == 0)) 171 | break; 172 | const char* dll_filename = GetPtrFromRVA(importDesc->Name, image->FileHeader, image->MappedAddress); 173 | libray_analyse(dll_db, dll_filename); 174 | importDesc++; 175 | } 176 | } 177 | ImageUnload(image); 178 | } 179 | return; 180 | } 181 | 182 | 183 | static libray_status_t* libray_analyse_really(hash_t* dll_db, const char* filename) { 184 | libray_status_t* status = libray_try_load(filename); 185 | hash_set_value(dll_db, strdup(filename), status); 186 | if (*status == LIBRARY_DLL_MISSING_DEPENDENCIES) 187 | libray_analyse_deeply(dll_db, filename); 188 | 189 | libray_log_status(filename, status); 190 | 191 | return status; 192 | } 193 | 194 | 195 | static libray_status_t* libray_analyse(hash_t* dll_db, const char* filename) { 196 | libray_status_t* status; 197 | 198 | status = hash_get_value(dll_db, filename); 199 | if (!status) 200 | status = libray_analyse_really(dll_db, filename); 201 | 202 | return status; 203 | } 204 | #endif /* WIN32 */ 205 | -------------------------------------------------------------------------------- /container/library.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 2022-2024 Nicolas.LAURENT@Renault.com 3 | * 4 | * DLL/SO loading functions. 5 | */ 6 | 7 | #ifndef LIBRARY_H 8 | #define LIBRARY_H 9 | 10 | # ifdef __cplusplus 11 | extern "C" { 12 | # endif 13 | 14 | #ifdef WIN32 15 | # include 16 | #endif 17 | 18 | /*---------------------------------------------------------------------------- 19 | L I B R A R Y _ T 20 | ----------------------------------------------------------------------------*/ 21 | 22 | #ifdef WIN32 23 | typedef HINSTANCE library_t; 24 | #else 25 | typedef void* library_t; 26 | #endif 27 | 28 | 29 | /*---------------------------------------------------------------------------- 30 | P R O T O T Y P E S 31 | ----------------------------------------------------------------------------*/ 32 | 33 | extern void* library_symbol(library_t library, const char *symbol_name); 34 | extern library_t library_load(const char* library_filename); 35 | extern void library_unload(library_t library); 36 | 37 | # ifdef __cplusplus 38 | } 39 | # endif 40 | #endif 41 | -------------------------------------------------------------------------------- /container/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "fmi2Functions.h" 7 | 8 | #include "container.h" 9 | 10 | static const container_t *logger_container = NULL; 11 | 12 | void logger_init(const container_t *container) { 13 | logger_container = container; 14 | } 15 | 16 | void logger(fmi2Status status, const char *message, ...) { 17 | va_list ap; 18 | va_start(ap, message); 19 | if (logger_container) { 20 | char buffer[4096]; 21 | 22 | vsnprintf(buffer, sizeof(buffer), message, ap); 23 | va_end(ap); 24 | 25 | if ((status != fmi2OK) || (logger_container->debug)) { 26 | logger_container->logger(logger_container->environment, 27 | logger_container->instance_name, 28 | status, NULL, "%s", buffer); 29 | } 30 | } else { 31 | vprintf(message, ap); 32 | } 33 | 34 | return; 35 | } 36 | 37 | 38 | void logger_embedded_fmu(fmu_t *fmu, 39 | fmi2String instanceName, fmi2Status status, 40 | fmi2String category, fmi2String message, ...) { 41 | const container_t *container = fmu->container; 42 | char buffer[4096]; 43 | va_list ap; 44 | va_start(ap, message); 45 | vsnprintf(buffer, sizeof(buffer), message, ap); 46 | va_end(ap); 47 | 48 | if ((status != fmi2OK) || (container->debug)) 49 | container->logger(container->environment, container->instance_name, status, NULL, "%s: %s", 50 | fmu->name, buffer); 51 | /*logger(status, "logger_embedded(name=%s, instance=%s, status=%d msg=%s", 52 | fmu->name, instanceName, status, buffer);*/ 53 | 54 | return; 55 | } 56 | -------------------------------------------------------------------------------- /container/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGGER_H 2 | # define LOGGER_H 3 | 4 | # ifdef __cplusplus 5 | extern "C" { 6 | # endif 7 | 8 | #include "fmi2Functions.h" 9 | 10 | #include "container.h" 11 | 12 | 13 | /*---------------------------------------------------------------------------- 14 | P R O T O T Y P E S 15 | ----------------------------------------------------------------------------*/ 16 | 17 | void logger_init(const container_t *container); 18 | void logger(fmi2Status status, const char *message, ...); 19 | void logger_embedded_fmu(fmu_t *fmu, 20 | fmi2String instanceName, fmi2Status status, 21 | fmi2String category, fmi2String message, ...); 22 | 23 | # ifdef __cplusplus 24 | } 25 | # endif 26 | #endif 27 | -------------------------------------------------------------------------------- /container/profile.c: -------------------------------------------------------------------------------- 1 | #ifdef WIN32 2 | # include 3 | #else 4 | # include 5 | #endif 6 | #include 7 | #include 8 | 9 | #include "profile.h" 10 | 11 | 12 | profile_t *profile_new(void) { 13 | profile_t *profile = malloc(sizeof(*profile)); 14 | 15 | profile->current_tic = 0; 16 | profile->total_elapsed = 0.0; 17 | 18 | return profile; 19 | } 20 | 21 | void profile_free(profile_t *profile) { 22 | if (profile) { 23 | free(profile); 24 | } 25 | return; 26 | } 27 | 28 | 29 | void profile_tic(profile_t *profile) { 30 | #ifdef WIN32 31 | profile->current_tic = GetTickCount(); 32 | #else 33 | struct timespec ts; 34 | unsigned theTick = 0U; 35 | clock_gettime(CLOCK_REALTIME, &ts); 36 | profile->current_tic = ts.tv_nsec / 1000000; 37 | profile->current_tic += ts.tv_sec * 1000; 38 | #endif 39 | 40 | return; 41 | } 42 | 43 | 44 | double profile_toc(profile_t *profile, double current_time) { 45 | profile_tic_t now; 46 | 47 | #ifdef WIN32 48 | now = GetTickCount(); 49 | #else 50 | struct timespec ts; 51 | unsigned theTick = 0U; 52 | clock_gettime(CLOCK_REALTIME, &ts); 53 | now = ts.tv_nsec / 1000000; 54 | now += ts.tv_sec * 1000; 55 | #endif 56 | 57 | profile->total_elapsed += (now - profile->current_tic) / 1000.0; /* seconds */ 58 | if (profile->total_elapsed > 1e-3) 59 | return current_time / profile->total_elapsed; 60 | else 61 | return current_time / 1e-3; 62 | } 63 | 64 | -------------------------------------------------------------------------------- /container/profile.h: -------------------------------------------------------------------------------- 1 | #ifndef PROFILE_H 2 | # define PROFILE_H 3 | 4 | # ifdef __cplusplus 5 | extern "C" { 6 | # endif 7 | 8 | /*----------------------------------------------------------------------------- 9 | P R O F I L E _ T 10 | -----------------------------------------------------------------------------*/ 11 | 12 | typedef unsigned int profile_tic_t; 13 | 14 | typedef struct { 15 | profile_tic_t current_tic; /* ms */ 16 | double total_elapsed; /* s */ 17 | } profile_t; 18 | 19 | 20 | /*---------------------------------------------------------------------------- 21 | P R O T O T Y P E S 22 | ----------------------------------------------------------------------------*/ 23 | 24 | extern profile_t *profile_new(void); 25 | extern void profile_free(profile_t *profile); 26 | extern void profile_tic(profile_t *profile); 27 | extern double profile_toc(profile_t *profile, double current_time); 28 | 29 | # ifdef __cplusplus 30 | } 31 | # endif 32 | #endif 33 | -------------------------------------------------------------------------------- /container/thread.c: -------------------------------------------------------------------------------- 1 | #include "thread.h" 2 | 3 | 4 | thread_t thread_new(thread_function_t function, void *data) { 5 | #ifdef WIN32 6 | HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)function, data, 0, NULL); /* Thread should be create _after_ mutexes */ 7 | SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); 8 | SetThreadPriority(thread, THREAD_PRIORITY_HIGHEST); 9 | SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL); /* Try RT ! */ 10 | #else 11 | pthread_t thread; 12 | pthread_create(&thread, NULL, (void *(*)(void*))function, data); 13 | #endif 14 | return thread; 15 | } 16 | 17 | 18 | void thread_join(thread_t thread) { 19 | #ifdef WIN32 20 | WaitForSingleObject(thread, INFINITE); 21 | CloseHandle(thread); 22 | #else 23 | pthread_join(thread, NULL); 24 | #endif 25 | 26 | 27 | 28 | #ifdef WIN32 29 | 30 | #endif 31 | } 32 | 33 | 34 | mutex_t thread_mutex_new(void) { 35 | #ifdef WIN32 36 | return CreateEventA(NULL, FALSE, FALSE, NULL); 37 | #else 38 | pthread_mutex_t mutex; 39 | pthread_mutex_init(&mutex, NULL); 40 | return mutex; 41 | #endif 42 | } 43 | 44 | 45 | void thread_mutex_free(mutex_t *mutex) { 46 | #ifdef WIN32 47 | CloseHandle(*mutex); 48 | #else 49 | pthread_mutex_destroy(mutex); 50 | #endif 51 | 52 | return; 53 | } 54 | 55 | 56 | void thread_mutex_lock(mutex_t *mutex) { 57 | #ifdef WIN32 58 | WaitForSingleObject(*mutex, INFINITE); 59 | #else 60 | pthread_mutex_lock(mutex); 61 | #endif 62 | 63 | return; 64 | } 65 | 66 | 67 | void thread_mutex_unlock(mutex_t *mutex) { 68 | #ifdef WIN32 69 | SetEvent(*mutex); 70 | #else 71 | pthread_mutex_unlock(mutex); 72 | #endif 73 | 74 | return; 75 | } 76 | -------------------------------------------------------------------------------- /container/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_H 2 | # define THREAD_H 3 | 4 | # ifdef __cplusplus 5 | extern "C" { 6 | # endif 7 | 8 | # ifdef WIN32 9 | # include 10 | # else 11 | # include 12 | # endif 13 | 14 | # ifdef WIN32 15 | typedef HANDLE thread_t; 16 | typedef HANDLE mutex_t; 17 | # else 18 | typedef pthread_t thread_t; 19 | typedef pthread_mutex_t mutex_t; 20 | # endif 21 | 22 | typedef void *(*thread_function_t)(void *); 23 | 24 | /*---------------------------------------------------------------------------- 25 | P R O T O T Y P E S 26 | ----------------------------------------------------------------------------*/ 27 | 28 | extern thread_t thread_new(thread_function_t function, void *data); 29 | extern void thread_join(thread_t thread); 30 | extern mutex_t thread_mutex_new(void); 31 | extern void thread_mutex_free(mutex_t *mutex); 32 | extern void thread_mutex_lock(mutex_t *mutex); 33 | extern void thread_mutex_unlock(mutex_t *mutex); 34 | 35 | # ifdef __cplusplus 36 | } 37 | # endif 38 | #endif 39 | -------------------------------------------------------------------------------- /doc/FMUContainer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/doc/FMUContainer.png -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | ## FMU Manipulation Toolbox validation plan 2 | 3 | Flowchart of a typical usage of FMU Manipulation Toolbox: 4 | ```mermaid 5 | flowchart LR 6 | fmu_in["Input FMU"] 7 | fmumanipulationtoolbox[["FMU Manipulation Toolbox"]] 8 | fmu_out["Output FMU"] 9 | fmu_in --> fmumanipulationtoolbox 10 | fmumanipulationtoolbox --> fmu_out 11 | ``` 12 | 13 | Test suite is implemented in [tests directory](../tests). 14 | 15 | ### FMU Import Compatibility information 16 | FMU Manipulation Toolbox import supports FMI-2.0 and Co-Simulation interface. 17 | 18 | #### Tested Exporting Tool 19 | - Amesim 20 | - Simulink 21 | - [Reference FMUs](https://github.com/modelica/Reference-FMUs) 22 | 23 | Automated testsuite use [bouncing_ball.fmu](../tests/operations/bouncing_ball.fmu). 24 | 25 | 26 | ### FMU Export Compatibility information 27 | FMU Manipulation Toolbox export supports FMI-2.0 and implements Co-Simulation interface. 28 | 29 | #### Validation Tools 30 | - fmpy 31 | 32 | #### Tested Importing Tools 33 | - Amesim 34 | - ControlBuild 35 | - Simulink 36 | -------------------------------------------------------------------------------- /doc/checker.md: -------------------------------------------------------------------------------- 1 | # FMU Checker 2 | 3 | FMU Manipulation Toolbox comes with a (very) basic checker for FMU. You can invoke it with `-check` option or with 4 | dedicated button in the GUI. 5 | 6 | ## Add your own checker 7 | 8 | ### Write your own checker 9 | 10 | In FMU Manipulation Toolbox a checker is written as a class derived from `OperationAbstract`. 11 | You can use `OperationGenericCheck` as model. Your class can be stored in python file stored anywhere. 12 | Every class derived from `OperationAbstract` will be considered as a checker and can be run during checks runtime. 13 | 14 | 15 | ### Run `fmutool` with your checker 16 | 17 | In order to let `fmutool` know where the file implementing your checker are, you can use 18 | `fmu_manipulation_toolbox.checker.add_from_file` function before invoking `fmutool`. The pointed file may contain 19 | multiple classes derivative from `OperationAbstract`. All of them will be run during the checks runtime. 20 | -------------------------------------------------------------------------------- /doc/container-json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/doc/container-json.png -------------------------------------------------------------------------------- /doc/container.md: -------------------------------------------------------------------------------- 1 | # FMU Containers ? 2 | 3 | A FMU Container is classical FMU which embeds other's FMU's: 4 | 5 | ![FMU Container](../doc/FMUContainer.png "FMU Container") 6 | FMU Manipulation Toolbox ships `fmucontainer` command which makes easy to embed FMU's into FMU. 7 | 8 | 9 | # How to create an FMU Container ? 10 | 11 | The `fmucontainer` command creates container from a description file. This file contains multiple parameters: 12 | 13 | - *time_step*: a FMU Container acts as a fixed step time "solver" for its embedded FMU's. 14 | - optional features 15 | - *multi-threading*: each embedded FMU will use its own thread to parallelize `doStep()` operation. 16 | - *profiling*: performance indicators can be calculated by the container to help to identify the bottlenecks among 17 | the embedded FMU's. 18 | - routing table 19 | - Input ports: the list of container's inputs. Each input is linked to one input of one of the embedded FMU's. 20 | - Output ports: the list of container's outputs. Each output is linked to one output of one of the embedded FMU's. 21 | - Connexions between embedded FMU's 22 | - Explicitly ignored ports (only port with `causality = "output"` can be ignored) 23 | - Automatic routing table 24 | - *auto_input* exposes automatically the (unconnected) ports of embedded FMU's with `causality = "input"` 25 | - *auto_output* expose automatically the (unconnected) ports of embedded FMU's with `causality = "output"` 26 | - *auto_local* expose automatically the ports of embedded FMU's with `causality = "local"` 27 | - *auto_parameter* expose automatically the ports of embedded FMU's with `causality = "parameter"` 28 | - *auto_link* links automatically ports of embedded FMU's which are left previously unconnected and have the same 29 | names and types 30 | 31 | Some of these parameters can be defined by Command Line Interface or by the input files. 32 | 33 | Several formats are supported as description files: 34 | - a `CSV` format: define only the routing table. Other options are defined as command line options. 35 | - a `JSON` file: all parameters can be defined in the file. Command line can define default values if the parameter 36 | is not present in `JSON` file. 37 | - a `SSP` file. See [ssp-standard.org](https://ssp-standard.org). define only the routing table. Other options are defined with command line. 38 | 39 | ![Routing](routing.png "Routing table") 40 | 41 | ## CSV Input file 42 | 43 | Example: 44 | * Two FMU's to be assembled: `bb_position.fmu` and `bb_velocity.fmu` 45 | * Container should expose `position1` from `bb_position.fmu` as `position` 46 | * Container has no input 47 | * there's two links between embedded FMU's 48 | 49 | 50 | the `container.csv` file content: 51 | 52 | ```csv 53 | rule;from_fmu;from_port;to_fmu;to_port 54 | FMU;bb_position.fmu;;; 55 | FMU;bb_velocity.fmu;;; 56 | OUTPUT;bb_position.fmu;position1;;position 57 | LINK;bb_position.fmu;is_ground;bb_velocity.fmu;reset 58 | LINK;bb_velocity.fmu;velocity;bb_position.fmu;velocity 59 | OUTPUT;bb_velocity.fmu;velocity;; 60 | ``` 61 | 62 | Asserting the FMU's and the CSV files are located in current working directory, 63 | the following command will build the container: 64 | 65 | ``` 66 | fmucontainer -container container.csv 67 | ``` 68 | 69 | Then `container.fmu` should be available. 70 | 71 | 72 | ## Json Input file 73 | 74 | ![Routing](container-json.png "Json input file") 75 | 76 | ``` 77 | fmucontainer -container container.json 78 | ``` 79 | 80 | ## SSP Input file 81 | 82 | This feature is still alpha. 83 | 84 | The following command 85 | ``` 86 | fmucontainer -container my_file.ssp 87 | ``` 88 | 89 | will 90 | 1. extact FMU's from the SSP file 91 | 2. build a container accordingly to the SSP 92 | 93 | 94 | -------------------------------------------------------------------------------- /doc/example.py: -------------------------------------------------------------------------------- 1 | from fmu_manipulation_toolbox.fmu_operations import FMU, OperationSaveNamesToCSV, OperationStripTopLevel, \ 2 | OperationRenameFromCSV, OperationAddRemotingWin32, OperationGetNames 3 | # 1st Use Case: remove toplevel bus (if any) 4 | fmu = FMU("tests/bouncing_ball.fmu") 5 | operation = OperationStripTopLevel() 6 | fmu.apply_operation(operation) 7 | fmu.repack(r"tests/bouncing_ball-no-tl.fmu") 8 | 9 | # 2nd Use Case: Extract names and write a CSV 10 | fmu = FMU("tests/bouncing_ball.fmu") 11 | operation = OperationSaveNamesToCSV("tests/bouncing_ball.csv") 12 | fmu.apply_operation(operation) 13 | 14 | # 3rd Use Case: Read CSV and rename FMU ports 15 | fmu = FMU("tests/bouncing_ball.fmu") 16 | operation = OperationRenameFromCSV("tests/bouncing_ball-modified.csv") 17 | fmu.apply_operation(operation) 18 | fmu.repack("tests/bouncing_ball-renamed.fmu") 19 | 20 | # 4th Use Case: Add remoting 21 | fmu = FMU("tests/bouncing_ball.fmu") 22 | operation = OperationAddRemotingWin32() 23 | fmu.apply_operation(operation) 24 | fmu.repack("tests/bouncing_ball-win32.fmu") 25 | 26 | fmu = FMU("tests/bouncing_ball-renamed.fmu") 27 | fmu.apply_operation(OperationGetNames()) 28 | -------------------------------------------------------------------------------- /doc/gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/doc/gui.png -------------------------------------------------------------------------------- /doc/remoting.md: -------------------------------------------------------------------------------- 1 | # Remoting 2 | 3 | ## What is it ? 4 | 5 | The remoting feature lets you to implement an additional interface to an existing FMU. 6 | There is 3 use cases: 7 | 1. Add a different bitness interface. For example, add a Windows 64bits interface to a existing 32bits only FMU. 8 | 2. Encapsulate the DLL of an existing FMU inside a dedicated process. This process will communicate with the simulation 9 | master. 10 | 3. Add a different OS interface. This feature is under study. 11 | 12 | 13 | ## Limitation 14 | 15 | Currently, only Co-simulation mode for FMI 2.0 is supported. 16 | 17 | 18 | ## Available configurations 19 | 20 | | FMU \ master | Windows 32bits | Windows 64bits | Linux 32bit | Linux 64bits | 21 | |----------------|-----------------------|-----------------------|---------------------------------------|---------------------------------------| 22 | | Windows 32bits | `-add-frontend-win32` | `-add-remoting-win64` | - | - | 23 | | Windows 64bits | `-add-remoting-win32` | `-add-frontend-win64` | - | - | 24 | | Linux 32bits | - | - | See `OperationAddRemotingWinAbstract` | See `OperationAddRemotingWinAbstract` | 25 | | Linux 64bits | - | - | See `OperationAddRemotingWinAbstract` | See `OperationAddRemotingWinAbstract` | 26 | -------------------------------------------------------------------------------- /doc/routing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/doc/routing.png -------------------------------------------------------------------------------- /fmi/fmi2TypesPlatform.h: -------------------------------------------------------------------------------- 1 | #ifndef fmi2TypesPlatform_h 2 | #define fmi2TypesPlatform_h 3 | 4 | /* Standard header file to define the argument types of the 5 | functions of the Functional Mock-up Interface 2.0.1 6 | This header file must be utilized both by the model and 7 | by the simulation engine. 8 | 9 | Revisions: 10 | - Sep. 29, 2019: License changed to 2-clause BSD License (without extensions) 11 | - Apr. 9, 2014: All prefixes "fmi" renamed to "fmi2" (decision from April 8) 12 | - Mar 31, 2014: New datatype fmiChar introduced. 13 | - Feb. 17, 2013: Changed fmiTypesPlatform from "standard32" to "default". 14 | Removed fmiUndefinedValueReference since no longer needed 15 | (because every state is defined in ScalarVariables). 16 | - March 20, 2012: Renamed from fmiPlatformTypes.h to fmiTypesPlatform.h 17 | - Nov. 14, 2011: Use the header file "fmiPlatformTypes.h" for FMI 2.0 18 | both for "FMI for model exchange" and for "FMI for co-simulation" 19 | New types "fmiComponentEnvironment", "fmiState", and "fmiByte". 20 | The implementation of "fmiBoolean" is change from "char" to "int". 21 | The #define "fmiPlatform" changed to "fmiTypesPlatform" 22 | (in order that #define and function call are consistent) 23 | - Oct. 4, 2010: Renamed header file from "fmiModelTypes.h" to fmiPlatformTypes.h" 24 | for the co-simulation interface 25 | - Jan. 4, 2010: Renamed meModelTypes_h to fmiModelTypes_h (by Mauss, QTronic) 26 | - Dec. 21, 2009: Changed "me" to "fmi" and "meModel" to "fmiComponent" 27 | according to meeting on Dec. 18 (by Martin Otter, DLR) 28 | - Dec. 6, 2009: Added meUndefinedValueReference (by Martin Otter, DLR) 29 | - Sept. 9, 2009: Changes according to FMI-meeting on July 21: 30 | Changed "version" to "platform", "standard" to "standard32", 31 | Added a precise definition of "standard32" as comment 32 | (by Martin Otter, DLR) 33 | - July 19, 2009: Added "me" as prefix to file names, added meTrue/meFalse, 34 | and changed meValueReferenced from int to unsigned int 35 | (by Martin Otter, DLR). 36 | - March 2, 2009: Moved enums and function pointer definitions to 37 | ModelFunctions.h (by Martin Otter, DLR). 38 | - Dec. 3, 2008 : First version by Martin Otter (DLR) and 39 | Hans Olsson (Dynasim). 40 | 41 | 42 | Copyright (C) 2008-2011 MODELISAR consortium, 43 | 2012-2019 Modelica Association Project "FMI" 44 | All rights reserved. 45 | 46 | This file is licensed by the copyright holders under the 2-Clause BSD License 47 | (https://opensource.org/licenses/BSD-2-Clause): 48 | 49 | ---------------------------------------------------------------------------- 50 | Redistribution and use in source and binary forms, with or without 51 | modification, are permitted provided that the following conditions are met: 52 | 53 | - Redistributions of source code must retain the above copyright notice, 54 | this list of conditions and the following disclaimer. 55 | 56 | - Redistributions in binary form must reproduce the above copyright notice, 57 | this list of conditions and the following disclaimer in the documentation 58 | and/or other materials provided with the distribution. 59 | 60 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 61 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 62 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 63 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 64 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 65 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 66 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 67 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 68 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 69 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 70 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 71 | ---------------------------------------------------------------------------- 72 | */ 73 | 74 | /* Platform (unique identification of this header file) */ 75 | #define fmi2TypesPlatform "default" 76 | 77 | /* Type definitions of variables passed as arguments 78 | Version "default" means: 79 | 80 | fmi2Component : an opaque object pointer 81 | fmi2ComponentEnvironment: an opaque object pointer 82 | fmi2FMUstate : an opaque object pointer 83 | fmi2ValueReference : handle to the value of a variable 84 | fmi2Real : double precision floating-point data type 85 | fmi2Integer : basic signed integer data type 86 | fmi2Boolean : basic signed integer data type 87 | fmi2Char : character data type 88 | fmi2String : a pointer to a vector of fmi2Char characters 89 | ('\0' terminated, UTF8 encoded) 90 | fmi2Byte : smallest addressable unit of the machine, typically one byte. 91 | */ 92 | typedef void* fmi2Component; /* Pointer to FMU instance */ 93 | typedef void* fmi2ComponentEnvironment; /* Pointer to FMU environment */ 94 | typedef void* fmi2FMUstate; /* Pointer to internal FMU state */ 95 | typedef unsigned int fmi2ValueReference; 96 | typedef double fmi2Real ; 97 | typedef int fmi2Integer; 98 | typedef int fmi2Boolean; 99 | typedef char fmi2Char; 100 | typedef const fmi2Char* fmi2String; 101 | typedef char fmi2Byte; 102 | 103 | /* Values for fmi2Boolean */ 104 | #define fmi2True 1 105 | #define fmi2False 0 106 | 107 | 108 | #endif /* fmi2TypesPlatform_h */ 109 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | def gui(): 5 | try: 6 | from .gui import main 7 | main() 8 | except ModuleNotFoundError as e: 9 | print(f"FATAL ERROR: {e}. No GUI Available.") 10 | 11 | 12 | def cli(): 13 | from .cli import fmutool 14 | fmutool() 15 | 16 | 17 | def main(): 18 | if len(sys.argv) == 1: 19 | gui() 20 | else: 21 | cli() 22 | 23 | 24 | if __name__ == '__main__': 25 | main() 26 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/checker.py: -------------------------------------------------------------------------------- 1 | import importlib.util 2 | import inspect 3 | import os 4 | import xmlschema 5 | from xmlschema.validators.exceptions import XMLSchemaValidationError 6 | from .fmu_operations import OperationAbstract 7 | 8 | 9 | class OperationGenericCheck(OperationAbstract): 10 | SUPPORTED_FMI_VERSIONS = ('2.0',) 11 | 12 | def __init__(self): 13 | self.compliant_with_version = None 14 | 15 | def __repr__(self): 16 | return f"FMU Generic Conformity Checks" 17 | 18 | def fmi_attrs(self, attrs): 19 | if attrs['fmiVersion'] not in self.SUPPORTED_FMI_VERSIONS: 20 | print(f"ERROR: Expected FMI {','.join(self.SUPPORTED_FMI_VERSIONS)} versions.") 21 | return 22 | 23 | xsd_filename = os.path.join(os.path.dirname(__file__), "resources", "fmi-" + attrs['fmiVersion'], 24 | "fmi2ModelDescription.xsd") 25 | try: 26 | xmlschema.validate(self.fmu.descriptor_filename, schema=xsd_filename) 27 | except XMLSchemaValidationError as error: 28 | print(error.reason, error.msg) 29 | else: 30 | self.compliant_with_version = attrs['fmiVersion'] 31 | 32 | def closure(self): 33 | if self.compliant_with_version: 34 | print(f"INFO: This FMU seems to be compliant with FMI-{self.compliant_with_version}.") 35 | else: 36 | print(f"ERROR: This FMU does not validate with FMI standard.") 37 | 38 | 39 | checker_list = [OperationGenericCheck] 40 | 41 | 42 | def add_from_file(checker_filename: str): 43 | spec = importlib.util.spec_from_file_location(checker_filename, checker_filename) 44 | if not spec: 45 | print(f"ERROR: Cannot load {checker_filename}. Is this a python file?") 46 | return 47 | try: 48 | checker_module = importlib.util.module_from_spec(spec) 49 | try: 50 | spec.loader.exec_module(checker_module) 51 | except (ModuleNotFoundError, SyntaxError) as error: 52 | print(f"ERROR: Cannot load {checker_filename}: {error})") 53 | return 54 | 55 | for checker_name, checker_class in inspect.getmembers(checker_module, inspect.isclass): 56 | if OperationAbstract in checker_class.__bases__: 57 | checker_list.append(checker_class) 58 | print(f"Adding checker: {checker_filename}|{checker_name}") 59 | 60 | except AttributeError: 61 | print(f"ERROR: {checker_filename} should implement class 'OperationCheck'") 62 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/cli.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import logging 3 | import sys 4 | from colorama import Fore, Style, init 5 | 6 | from .fmu_operations import * 7 | from .fmu_container import FMUContainerError 8 | from .assembly import Assembly, AssemblyError 9 | from .checker import checker_list 10 | from .version import __version__ as version 11 | from .help import Help 12 | 13 | 14 | def setup_logger(): 15 | class CustomFormatter(logging.Formatter): 16 | def format(self, record): 17 | log_format = "%(levelname)-8s | %(message)s" 18 | format_per_level = { 19 | logging.DEBUG: str(Fore.BLUE) + log_format, 20 | logging.INFO: str(Fore.CYAN) + log_format, 21 | logging.WARNING: str(Fore.YELLOW) + log_format, 22 | logging.ERROR: str(Fore.RED) + log_format, 23 | logging.CRITICAL: str(Fore.RED + Style.BRIGHT) + log_format, 24 | } 25 | formatter = logging.Formatter(format_per_level[record.levelno]) 26 | return formatter.format(record) 27 | init() 28 | logger = logging.getLogger("fmu_manipulation_toolbox") 29 | handler = logging.StreamHandler(stream=sys.stdout) 30 | handler.setFormatter(CustomFormatter()) 31 | logger.addHandler(handler) 32 | logger.setLevel(logging.INFO) 33 | 34 | return logger 35 | 36 | 37 | def make_wide(formatter, w=120, h=36): 38 | """Return a wider HelpFormatter, if possible.""" 39 | try: 40 | # https://stackoverflow.com/a/5464440 41 | # beware: "Only the name of this class is considered a public API." 42 | kwargs = {'width': w, 'max_help_position': h} 43 | formatter(None, **kwargs) 44 | return lambda prog: formatter(prog, **kwargs) 45 | except TypeError: 46 | return formatter 47 | 48 | 49 | def fmutool(): 50 | print(f"FMU Manipulation Toolbox version {version}") 51 | help_message = Help() 52 | 53 | parser = argparse.ArgumentParser(prog='fmutool', 54 | description="Analyse and Manipulate a FMU by modifying its 'modelDescription.xml'", 55 | formatter_class=make_wide(argparse.HelpFormatter), 56 | add_help=False, 57 | epilog="see: https://github.com/grouperenault/fmu_manipulation_toolbox/blob/main/README.md") 58 | 59 | def add_option(option, *args, **kwargs): 60 | parser.add_argument(option, *args, help=help_message.usage(option), **kwargs) 61 | 62 | add_option('-h', '-help', action="help") 63 | 64 | # I/O 65 | add_option('-input', action='store', dest='fmu_input', default=None, required=True, metavar='path/to/module.fmu') 66 | add_option('-output', action='store', dest='fmu_output', default=None, metavar='path/to/module-modified.fmu') 67 | 68 | # Port name manipulation 69 | add_option('-remove-toplevel', action='append_const', dest='operations_list', const=OperationStripTopLevel()) 70 | add_option('-merge-toplevel', action='append_const', dest='operations_list', const=OperationMergeTopLevel()) 71 | add_option('-trim-until', action='append', dest='operations_list', type=OperationTrimUntil, metavar='prefix') 72 | add_option('-remove-regexp', action='append', dest='operations_list', type=OperationRemoveRegexp, 73 | metavar='regular-expression') 74 | add_option('-keep-only-regexp', action='append', dest='operations_list', type=OperationKeepOnlyRegexp, 75 | metavar='regular-expression') 76 | add_option('-remove-all', action='append_const', dest='operations_list', const=OperationRemoveRegexp('.*')) 77 | 78 | # Batch Rename 79 | add_option('-dump-csv', action='append', dest='operations_list', type=OperationSaveNamesToCSV, 80 | metavar='path/to/list.csv') 81 | add_option('-rename-from-csv', action='append', dest='operations_list', type=OperationRenameFromCSV, 82 | metavar='path/to/translation.csv') 83 | 84 | # Remoting 85 | add_option('-add-remoting-win32', action='append_const', dest='operations_list', const=OperationAddRemotingWin32()) 86 | add_option('-add-remoting-win64', action='append_const', dest='operations_list', const=OperationAddRemotingWin64()) 87 | add_option('-add-frontend-win32', action='append_const', dest='operations_list', const=OperationAddFrontendWin32()) 88 | add_option('-add-frontend-win64', action='append_const', dest='operations_list', const=OperationAddFrontendWin64()) 89 | 90 | # Extraction / Removal 91 | add_option('-extract-descriptor', action='store', dest='extract_description', 92 | metavar='path/to/saved-modelDescriptor.xml') 93 | add_option('-remove-sources', action='append_const', dest='operations_list', 94 | const=OperationRemoveSources()) 95 | # Filter 96 | add_option('-only-parameters', action='append_const', dest='apply_on', const='parameter') 97 | add_option('-only-inputs', action='append_const', dest='apply_on', const='input') 98 | add_option('-only-outputs', action='append_const', dest='apply_on', const='output') 99 | # Checker 100 | add_option('-summary', action='append_const', dest='operations_list', const=OperationSummary()) 101 | add_option('-check', action='append_const', dest='operations_list', const=[checker() for checker in checker_list]) 102 | 103 | cli_options = parser.parse_args() 104 | # handle the "no operation" use case 105 | if not cli_options.operations_list: 106 | cli_options.operations_list = [] 107 | 108 | if cli_options.fmu_input == cli_options.fmu_output: 109 | print(f"FATAL ERROR: '-input' and '-output' should point to different files.") 110 | sys.exit(-3) 111 | 112 | print(f"READING Input='{cli_options.fmu_input}'") 113 | try: 114 | fmu = FMU(cli_options.fmu_input) 115 | except FMUException as reason: 116 | print(f"FATAL ERROR: {reason}") 117 | sys.exit(-4) 118 | 119 | if cli_options.apply_on: 120 | print("Applying operation for :") 121 | for causality in cli_options.apply_on: 122 | print(f" - causality = {causality}") 123 | 124 | def flatten(list_of_list: list): 125 | return [x for xs in list_of_list for x in xs] 126 | 127 | for operation in flatten(cli_options.operations_list): 128 | print(f" => {operation}") 129 | try: 130 | fmu.apply_operation(operation, cli_options.apply_on) 131 | except OperationException as reason: 132 | print(f"ERROR: {reason}") 133 | sys.exit(-6) 134 | 135 | if cli_options.extract_description: 136 | print(f"WRITING ModelDescriptor='{cli_options.extract_description}'") 137 | fmu.save_descriptor(cli_options.extract_description) 138 | 139 | if cli_options.fmu_output: 140 | print(f"WRITING Output='{cli_options.fmu_output}'") 141 | try: 142 | fmu.repack(cli_options.fmu_output) 143 | except FMUException as reason: 144 | print(f"FATAL ERROR: {reason}") 145 | sys.exit(-5) 146 | else: 147 | print(f"INFO Modified FMU is not saved. If necessary use '-output' option.") 148 | 149 | 150 | def fmucontainer(): 151 | logger = setup_logger() 152 | 153 | logger.info(f"FMUContainer version {version}") 154 | parser = argparse.ArgumentParser(prog="fmucontainer", description="Generate FMU from FMU's", 155 | formatter_class=make_wide(argparse.ArgumentDefaultsHelpFormatter), 156 | add_help=False, 157 | epilog="see: https://github.com/grouperenault/fmu_manipulation_toolbox/blob/main/" 158 | "container/README.md") 159 | 160 | parser.add_argument('-h', '-help', action="help") 161 | 162 | parser.add_argument("-fmu-directory", action="store", dest="fmu_directory", required=False, default=".", 163 | help="Directory containing initial FMU’s and used to generate containers. " 164 | "If not defined, current directory is used.") 165 | 166 | parser.add_argument("-container", action="append", dest="container_descriptions_list", default=[], 167 | metavar="filename.{csv|json|ssp},[:step_size]", required=True, 168 | help="Description of the container to create.") 169 | 170 | parser.add_argument("-debug", action="store_true", dest="debug", 171 | help="Add lot of useful log during the process.") 172 | 173 | parser.add_argument("-no-auto-input", action="store_false", dest="auto_input", default=True, 174 | help="Create ONLY explicit input.") 175 | 176 | parser.add_argument("-no-auto-output", action="store_false", dest="auto_output", default=True, 177 | help="Create ONLY explicit output.") 178 | 179 | parser.add_argument("-auto-parameter", action="store_true", dest="auto_parameter", default=False, 180 | help="Expose parameters of the embedded fmu's.") 181 | 182 | parser.add_argument("-auto-local", action="store_true", dest="auto_local", default=False, 183 | help="Expose local variables of the embedded fmu's.") 184 | 185 | parser.add_argument("-no-auto-link", action="store_false", dest="auto_link", default=True, 186 | help="Create ONLY explicit links.") 187 | 188 | parser.add_argument("-mt", action="store_true", dest="mt", default=False, 189 | help="Enable Multi-Threaded mode for the generated container.") 190 | 191 | parser.add_argument("-profile", action="store_true", dest="profiling", default=False, 192 | help="Enable Profiling mode for the generated container.") 193 | 194 | parser.add_argument("-dump-json", action="store_true", dest="dump", default=False, 195 | help="Dump a JSON file for each container.") 196 | 197 | config = parser.parse_args() 198 | 199 | if config.debug: 200 | logger.setLevel(logging.DEBUG) 201 | 202 | fmu_directory = Path(config.fmu_directory) 203 | logger.info(f"FMU directory: '{fmu_directory}'") 204 | 205 | for description in config.container_descriptions_list: 206 | try: 207 | tokens = description.split(":") 208 | filename = ":".join(tokens[:-1]) 209 | step_size = float(tokens[-1]) 210 | except ValueError: 211 | step_size = None 212 | filename = description 213 | 214 | try: 215 | assembly = Assembly(filename, step_size=step_size, auto_link=config.auto_link, 216 | auto_input=config.auto_input, auto_output=config.auto_output, 217 | auto_local=config.auto_local, mt=config.mt, 218 | profiling=config.profiling, fmu_directory=fmu_directory, debug=config.debug, 219 | auto_parameter=config.auto_parameter) 220 | except FileNotFoundError as e: 221 | logger.fatal(f"Cannot read file: {e}") 222 | continue 223 | except (FMUContainerError, AssemblyError) as e: 224 | logger.fatal(f"{filename}: {e}") 225 | continue 226 | 227 | try: 228 | assembly.make_fmu(dump_json=config.dump) 229 | except FMUContainerError as e: 230 | logger.fatal(f"{filename}: {e}") 231 | continue 232 | 233 | 234 | if __name__ == "__main__": 235 | fmucontainer() 236 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/gui_style.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | if os.name == 'nt': 4 | gui_style = """ 5 | QWidget { 6 | font: 10pt "Verdana"; 7 | background: #4b4e51; 8 | color: #b5bab9; 9 | } 10 | QPushButton, QComboBox { 11 | min-height: 30px; 12 | padding: 1px 1px 0.2em 0.2em; 13 | border: 1px solid #282830; 14 | border-radius: 5px; 15 | color: #dddddd; 16 | } 17 | QPushButton:pressed { 18 | border: 2px solid #282830; 19 | } 20 | QPushButton.info { 21 | background-color: #4e6749; 22 | } 23 | QPushButton.info:hover { 24 | background-color: #5f7850; 25 | } 26 | QPushButton.modify { 27 | background-color: #98763f; 28 | } 29 | QPushButton.modify:hover { 30 | background-color: #a9874f; 31 | } 32 | QPushButton.removal { 33 | background-color: #692e2e; 34 | } 35 | QPushButton.removal:hover { 36 | background-color: #7a3f3f; 37 | } 38 | QPushButton.save { 39 | background-color: #564967; 40 | } 41 | QPushButton.save:hover { 42 | background-color: #675a78; 43 | } 44 | QPushButton.quit { 45 | background-color: #4571a4; 46 | } 47 | QPushButton.quit:hover { 48 | background-color: #5682b5; 49 | } 50 | QPushButton::disabled { 51 | background-color: gray; 52 | } 53 | QToolTip { 54 | color: black 55 | } 56 | QLabel.dropped_fmu { 57 | background-color: #b5bab9 58 | } 59 | QLabel.title { 60 | font: 14pt bold "Verdana"; 61 | } 62 | QLabel.dropped_fmu:hover { 63 | background-color: #c6cbca 64 | } 65 | QTextBrowser, QTreeView { 66 | font: 11pt "Consolas"; 67 | background-color: #282830; 68 | color: #b5bab9; 69 | } 70 | QMenu::item { 71 | padding: 2px 250px 2px 20px; 72 | border: 1px solid transparent; 73 | } 74 | QMenu::item::indicator, QCheckBox::item::indicator { 75 | width: 32px; 76 | height: 32px; 77 | } 78 | QMenu::indicator:checked, QCheckBox::indicator:checked { 79 | image: url(images:checkbox-checked.png); 80 | } 81 | QMenu::indicator:checked:hover, QCheckBox::indicator:checked:hover { 82 | image: url(images:checkbox-checked-hover.png); 83 | } 84 | QMenu::indicator:checked:disabled, QCheckBox::indicator:checked:disabled { 85 | image: url(images:checkbox-checked-disabled.png); 86 | } 87 | QMenu::indicator:unchecked, QCheckBox::indicator:unchecked { 88 | image: url(images:checkbox-unchecked.png); 89 | } 90 | QMenu::indicator:unchecked:hover, QCheckBox::indicator:unchecked:hover { 91 | image: url(images:checkbox-unchecked-hover.png); 92 | } 93 | QMenu::indicator:unchecked:disabled, QCheckBox::indicator:unchecked:disabled { 94 | image: url(images:checkbox-unchecked-disabled.png); 95 | } 96 | QCheckBox::item { 97 | padding: 2px 250px 2px 20px; 98 | border: 1px solid transparent; 99 | } 100 | QTabBar::tab { 101 | min-height: 30px; 102 | padding: 1px 1px 0.2em 0.2em; 103 | color: #dddddd; 104 | margin: 2px; 105 | margin-bottom: 0px; 106 | border: 1px solid #282830; 107 | border-top-left-radius: 5px; 108 | border-top-right-radius: 5px; 109 | } 110 | QTabBar::tab:selected, QTabBar::tab:hover { 111 | background-color: #5f7850; 112 | margin-bottom:-1px; 113 | } 114 | QTabBar { 115 | border-bottom: 1px solid #282830; 116 | } 117 | QTabBar::tab:top:last, QTabBar::tab:bottom:last { 118 | margin-right: 0; 119 | } 120 | QTabBar::tab:top:first, QTabBar::tab:bottom:first { 121 | margin-left: 0; 122 | } 123 | """ 124 | else: 125 | gui_style = """ 126 | QWidget { 127 | font: 12pt; 128 | background: #4b4e51; 129 | color: #b5bab9; 130 | } 131 | QPushButton, QComboBox { 132 | min-height: 30px; 133 | padding: 1px 1px 0.2em 0.2em; 134 | border: 1px solid #282830; 135 | border-radius: 5px; 136 | color: #dddddd; 137 | } 138 | QPushButton:pressed { 139 | border: 2px solid #282830; 140 | } 141 | QPushButton.info { 142 | background-color: #4e6749; 143 | } 144 | QPushButton.info:hover { 145 | background-color: #5f7850; 146 | } 147 | QPushButton.modify { 148 | background-color: #98763f; 149 | } 150 | QPushButton.modify:hover { 151 | background-color: #a9874f; 152 | } 153 | QPushButton.removal { 154 | background-color: #692e2e; 155 | } 156 | QPushButton.removal:hover { 157 | background-color: #7a3f3f; 158 | } 159 | QPushButton.save { 160 | background-color: #564967; 161 | } 162 | QPushButton.save:hover { 163 | background-color: #675a78; 164 | } 165 | QPushButton.quit { 166 | background-color: #4571a4; 167 | } 168 | QPushButton.quit:hover { 169 | background-color: #5682b5; 170 | } 171 | QPushButton::disabled { 172 | background-color: gray; 173 | } 174 | QToolTip { 175 | color: black 176 | } 177 | QLabel.dropped_fmu { 178 | background-color: #b5bab9 179 | } 180 | QLabel.title { 181 | font: 14pt bold "Verdana"; 182 | } 183 | QLabel.dropped_fmu:hover { 184 | background-color: #c6cbca 185 | } 186 | QTextBrowser, QTreeView { 187 | font: 14pt "Courier New"; 188 | background-color: #282830; 189 | color: #b5bab9; 190 | } 191 | QMenu::item { 192 | padding: 2px 250px 2px 20px; 193 | border: 1px solid transparent; 194 | } 195 | QMenu::item::indicator, QCheckBox::item::indicator { 196 | width: 32px; 197 | height: 32px; 198 | } 199 | QMenu::indicator:checked, QCheckBox::indicator:checked { 200 | image: url(images:checkbox-checked.png); 201 | } 202 | QMenu::indicator:checked:hover, QCheckBox::indicator:checked:hover { 203 | image: url(images:checkbox-checked-hover.png); 204 | } 205 | QMenu::indicator:checked:disabled, QCheckBox::indicator:checked:disabled { 206 | image: url(images:checkbox-checked-disabled.png); 207 | } 208 | QMenu::indicator:unchecked, QCheckBox::indicator:unchecked { 209 | image: url(images:checkbox-unchecked.png); 210 | } 211 | QMenu::indicator:unchecked:hover, QCheckBox::indicator:unchecked:hover { 212 | image: url(images:checkbox-unchecked-hover.png); 213 | } 214 | QMenu::indicator:unchecked:disabled, QCheckBox::indicator:unchecked:disabled { 215 | image: url(images:checkbox-unchecked-disabled.png); 216 | } 217 | QCheckBox::item { 218 | padding: 2px 250px 2px 20px; 219 | border: 1px solid transparent; 220 | } 221 | QTabBar::tab { 222 | min-height: 30px; 223 | padding: 1px 1px 0.2em 0.2em; 224 | color: #dddddd; 225 | margin: 2px; 226 | margin-bottom: 0px; 227 | border: 1px solid #282830; 228 | border-top-left-radius: 5px; 229 | border-top-right-radius: 5px; 230 | } 231 | QTabBar::tab:selected, QTabBar::tab:hover { 232 | background-color: #5f7850; 233 | margin-bottom:-1px; 234 | } 235 | QTabBar { 236 | border-bottom: 1px solid #282830; 237 | } 238 | QTabBar::tab:top:last, QTabBar::tab:bottom:last { 239 | margin-right: 0; 240 | } 241 | QTabBar::tab:top:first, QTabBar::tab:bottom:first { 242 | margin-left: 0; 243 | } 244 | """ 245 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/help.py: -------------------------------------------------------------------------------- 1 | class Help: 2 | _usage = { 3 | '-h': "display help.", 4 | '-input': "this option is mandatory to specify the filename of the FMU to be loaded.", 5 | 6 | '-output': "this option is used to specify the filename of the FMU to be created after manipulations." 7 | " If it is not provided, no new fmu will be saved and some manipulations can be lost.", 8 | 9 | '-remove-toplevel': "rename the ports of the input fmu by striping all characters until the first '.' " 10 | "(toplevel bus). If no '.' is present, the port won't be renamed. Resulting fmu should be " 11 | "saved by using -output option. Note: before version 1.2.6, this option was spelled " 12 | "-remove-toplel.", 13 | 14 | '-merge-toplevel': "replace first '.' by an '_' on every port name.", 15 | 16 | '-trim-until': "remove a prefix from port name. Example '-trim-until _' : will rename port names of the" 17 | " FMU by removing part of the name until the first '_'. Prefix can be longer than a " 18 | "single character. ", 19 | 20 | '-remove-regexp': "remove ports that match the regular-expression. Other ports will be kept. Resulting " 21 | "fmu should be saved by using -output option. This option is available from version 1.1. " 22 | "See https://en.wikipedia.org/wiki/Regular_expression to have more detail of expected " 23 | "format.", 24 | 25 | '-keep-only-regexp': "keep only ports that match the regular-expression. Other ports will be removed. " 26 | "Resulting fmu should be saved by using -output option. This option is available from " 27 | "version 1.1. See https://en.wikipedia.org/wiki/Regular_expression to have more detail " 28 | "of expected format.", 29 | 30 | '-remove-all': "equivalent to '-remove-regexp .*'. Typical use case is to use it with -only-* options. " 31 | "Example: in order ro suppress all parameters of FMU: -only-parameters -remove-all", 32 | 33 | '-dump-csv': "list all names of the ports of the input fmu and store them inside path/to/list.csv. " 34 | "This file is ';' separated. It contains two columns in order to be easily reused by " 35 | "-rename-from-csv option.", 36 | 37 | '-rename-from-csv': "rename the ports of fmu accordingly to path/to/translation.csv. This file is ';' " 38 | "separated. It contains two columns. First column contains original names. Second column " 39 | "contains new names. * If a port is not found in the file, it won't be renamed. This is " 40 | "working with version > 1.2.6. It is safer to keep ALL port in csv. * If the new name is" 41 | " empty, the port will be removed. This is working starting version 1.1. * If a name in " 42 | "the file is not present in input FMU, it will be ignored. (no warning will be issued). " 43 | "Resulting fmu should be saved by using -output option.", 44 | 45 | '-add-remoting-win32': "this option is windows specific. It will add 'win32' interface to a 'win64' fmu." 46 | " Please upgrade to version 1.2.1 before using this option. Resulting fmu should be" 47 | " saved by using -output option.", 48 | 49 | '-add-remoting-win64': "this option is windows specific. It will add 'win64' interface to a 'win32' fmu." 50 | " Please upgrade to version 1.2.1 before using this option. Resulting fmu should be" 51 | " saved by using -output option.", 52 | 53 | '-add-frontend-win32': "this option is windows specific. It can be used with 'win32' fmu. At simulation time, " 54 | "the FMU will spawn a dedicated process tu run the model. This option is available from " 55 | "version 1.4. Resulting fmu should be saved by using -output option.", 56 | 57 | '-add-frontend-win64': "this option is windows specific. It can be used with 'win64' fmu. At simulation time, " 58 | "the FMU will spawn a dedicated process tu run the model. This option is available from " 59 | "version 1.4. Resulting fmu should be saved by using -output option.", 60 | 61 | '-extract-descriptor': "save the modelDescription.xml into the specified location. If modification options " 62 | "(like -rename-from-csv or -remove-toplevel are set), the saved file will contain " 63 | "modification. This option is available from version 1.1.", 64 | 65 | '-remove-sources': "Remove sources folder from the FMU. This option is available from version 1.3.", 66 | 67 | '-only-parameters': "apply operation only on ports with causality = 'parameter'. This " 68 | "option is available from version 1.3.", 69 | 70 | '-only-inputs': "apply operation only on ports with causality = 'parameter'. This " 71 | "option is available from version 1.3.", 72 | 73 | '-only-outputs': "apply operation only on ports with causality = 'output'. This " 74 | "option is available from version 1.3.", 75 | 76 | '-summary': "display useful information regarding the FMU.", 77 | 78 | '-check': "performs some check of FMU and display Errors or Warnings. This is useful to avoid later " 79 | "issues when using the FMU.", 80 | 81 | # GUI message 82 | "gui-apply-only": "Apply operation only on ports with specified causality. If selected, at least one causality " 83 | "should be selected." 84 | } 85 | 86 | def usage(self, option): 87 | return self._usage[option] 88 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/checkbox-checked-disabled.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/checkbox-checked-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/checkbox-checked-hover.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/checkbox-checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/checkbox-checked.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/checkbox-unchecked-disabled.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/checkbox-unchecked-hover.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/checkbox-unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/checkbox-unchecked.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/container.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/darwin64/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/darwin64/.gitkeep -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/drop_fmu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/drop_fmu.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Annotation.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Copyright(c) 2008-2011 MODELISAR consortium, 6 | 2012-2022 Modelica Association Project "FMI". 7 | All rights reserved. 8 | 9 | This schema file is part of the FMI 2.0.4 standard. 10 | 11 | This file is licensed by the copyright holders under the 2-Clause BSD License 12 | (https://opensource.org/licenses/BSD-2-Clause): 13 | 14 | ---------------------------------------------------------------------------- 15 | Redistribution and use in source and binary forms, with or without 16 | modification, are permitted provided that the following conditions are met: 17 | 18 | - Redistributions of source code must retain the above copyright notice, 19 | this list of conditions and the following disclaimer. 20 | 21 | - Redistributions in binary form must reproduce the above copyright notice, 22 | this list of conditions and the following disclaimer in the documentation 23 | and/or other materials provided with the distribution. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 29 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 31 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 32 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 33 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 35 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | ---------------------------------------------------------------------------- 37 | 38 | 39 | 40 | 41 | 42 | 43 | Tool specific annotation (ignored by other tools). 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Name of tool that can interpret the annotation. "name" must be unique with respect to all other elements of the VendorAnnotation list. 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/fmi-2.0/fmi2AttributeGroups.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Copyright(c) 2008-2011 MODELISAR consortium, 6 | 2012-2022 Modelica Association Project "FMI". 7 | All rights reserved. 8 | 9 | This schema file is part of the FMI 2.0.4 standard. 10 | 11 | This file is licensed by the copyright holders under the 2-Clause BSD License 12 | (https://opensource.org/licenses/BSD-2-Clause): 13 | 14 | ---------------------------------------------------------------------------- 15 | Redistribution and use in source and binary forms, with or without 16 | modification, are permitted provided that the following conditions are met: 17 | 18 | - Redistributions of source code must retain the above copyright notice, 19 | this list of conditions and the following disclaimer. 20 | 21 | - Redistributions in binary form must reproduce the above copyright notice, 22 | this list of conditions and the following disclaimer in the documentation 23 | and/or other materials provided with the distribution. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 29 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 31 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 32 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 33 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 35 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | ---------------------------------------------------------------------------- 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Default display unit, provided the conversion of values in "unit" to values in "displayUnit" is defined in UnitDefinitions / Unit / DisplayUnit. 45 | 46 | 47 | 48 | 49 | If relativeQuantity=true, offset for displayUnit must be ignored. 50 | 51 | 52 | 53 | 54 | 55 | max >= min required 56 | 57 | 58 | 59 | 60 | nominal > 0.0 required 61 | 62 | 63 | 64 | 65 | Set to true, e.g., for crank angle. If true and variable is a state, relative tolerance should be zero on this variable. 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | max >= min required 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/fmi-2.0/fmi2ScalarVariable.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Copyright(c) 2008-2011 MODELISAR consortium, 8 | 2012-2022 Modelica Association Project "FMI". 9 | All rights reserved. 10 | 11 | This schema file is part of the FMI 2.0.4 standard. 12 | 13 | This file is licensed by the copyright holders under the 2-Clause BSD License 14 | (https://opensource.org/licenses/BSD-2-Clause): 15 | 16 | ---------------------------------------------------------------------------- 17 | Redistribution and use in source and binary forms, with or without 18 | modification, are permitted provided that the following conditions are met: 19 | 20 | - Redistributions of source code must retain the above copyright notice, 21 | this list of conditions and the following disclaimer. 22 | 23 | - Redistributions in binary form must reproduce the above copyright notice, 24 | this list of conditions and the following disclaimer in the documentation 25 | and/or other materials provided with the distribution. 26 | 27 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 31 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 32 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 33 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 34 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 35 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 36 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 37 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | ---------------------------------------------------------------------------- 39 | 40 | 41 | 42 | 43 | Properties of a scalar variable 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | If present, name of type defined with TypeDefinitions / SimpleType providing defaults. 52 | 53 | 54 | 55 | 56 | 57 | Value before initialization, if initial=exact or approx. 58 | max >= start >= min required 59 | 60 | 61 | 62 | 63 | If present, this variable is the derivative of variable with ScalarVariable index "derivative". 64 | 65 | 66 | 67 | 68 | Only for ModelExchange and if variable is a continuous-time state: 69 | If true, state can be reinitialized at an event by the FMU 70 | If false, state will never be reinitialized at an event by the FMU 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | If present, name of type defined with TypeDefinitions / SimpleType providing defaults. 80 | 81 | 82 | 83 | 84 | 85 | Value before initialization, if initial=exact or approx. 86 | max >= start >= min required 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | If present, name of type defined with TypeDefinitions / SimpleType providing defaults. 96 | 97 | 98 | 99 | 100 | Value before initialization, if initial=exact or approx 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | If present, name of type defined with TypeDefinitions / SimpleType providing defaults. 110 | 111 | 112 | 113 | 114 | Value before initialization, if initial=exact or approx 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | Name of type defined with TypeDefinitions / SimpleType 124 | 125 | 126 | 127 | 128 | 129 | 130 | max >= min required 131 | 132 | 133 | 134 | 135 | Value before initialization, if initial=exact or approx. 136 | max >= start >= min required 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | Additional data of the scalar variable, e.g., for the dialog menu or the graphical layout 145 | 146 | 147 | 148 | 149 | 150 | Identifier of variable, e.g. "a.b.mod[3,4].'#123'.c". "name" must be unique with respect to all other elements of the ModelVariables list 151 | 152 | 153 | 154 | 155 | Identifier for variable value in FMI2 function calls (not necessarily unique with respect to all variables) 156 | 157 | 158 | 159 | 160 | 161 | parameter: independent parameter 162 | calculatedParameter: calculated parameter 163 | input/output: can be used in connections 164 | local: variable calculated from other variables 165 | independent: independent variable (usually time) 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | constant: value never changes 181 | fixed: value fixed after initialization 182 | tunable: value constant between external events 183 | discrete: value constant between internal events 184 | continuous: no restriction on value changes 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | exact: initialized with start value 199 | approx: iteration variable that starts with start value 200 | calculated: calculated from other variables. 201 | If not provided, initial is deduced from causality and variability (details see specification) 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | Only for ModelExchange and only for variables with variability = "input": 214 | If present with value = false, then only one fmi2SetXXX call is allowed at one super dense time instant. In other words, this input is not allowed to appear in an algebraic loop. 215 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Type.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Copyright(c) 2008-2011 MODELISAR consortium, 7 | 2012-2022 Modelica Association Project "FMI". 8 | All rights reserved. 9 | 10 | This schema file is part of the FMI 2.0.4 standard. 11 | 12 | This file is licensed by the copyright holders under the 2-Clause BSD License 13 | (https://opensource.org/licenses/BSD-2-Clause): 14 | 15 | ---------------------------------------------------------------------------- 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions are met: 18 | 19 | - Redistributions of source code must retain the above copyright notice, 20 | this list of conditions and the following disclaimer. 21 | 22 | - Redistributions in binary form must reproduce the above copyright notice, 23 | this list of conditions and the following disclaimer in the documentation 24 | and/or other materials provided with the distribution. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 30 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 31 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 32 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 33 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 34 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 35 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 36 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 | ---------------------------------------------------------------------------- 38 | 39 | 40 | 41 | 42 | Type attributes of a scalar variable 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Must be a unique number in the same enumeration 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | Name of SimpleType element. "name" must be unique with respect to all other elements of the TypeDefinitions list. Furthermore, "name" of a SimpleType must be different to all "name"s of ScalarVariable. 81 | 82 | 83 | 84 | 85 | Description of the SimpleType 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/fmi-2.0/fmi2Unit.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Copyright(c) 2008-2011 MODELISAR consortium, 6 | 2012-2022 Modelica Association Project "FMI". 7 | All rights reserved. 8 | 9 | This schema file is part of the FMI 2.0.4 standard. 10 | 11 | This file is licensed by the copyright holders under the 2-Clause BSD License 12 | (https://opensource.org/licenses/BSD-2-Clause): 13 | 14 | ---------------------------------------------------------------------------- 15 | Redistribution and use in source and binary forms, with or without 16 | modification, are permitted provided that the following conditions are met: 17 | 18 | - Redistributions of source code must retain the above copyright notice, 19 | this list of conditions and the following disclaimer. 20 | 21 | - Redistributions in binary form must reproduce the above copyright notice, 22 | this list of conditions and the following disclaimer in the documentation 23 | and/or other materials provided with the distribution. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 29 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 31 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 32 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 33 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 35 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | ---------------------------------------------------------------------------- 37 | 38 | 39 | 40 | 41 | Unit definition (with respect to SI base units) and default display units 42 | 43 | 44 | 45 | 46 | BaseUnit_value = factor*Unit_value + offset 47 | 48 | 49 | 50 | 51 | Exponent of SI base unit "kg" 52 | 53 | 54 | 55 | 56 | Exponent of SI base unit "m" 57 | 58 | 59 | 60 | 61 | Exponent of SI base unit "s" 62 | 63 | 64 | 65 | 66 | Exponent of SI base unit "A" 67 | 68 | 69 | 70 | 71 | Exponent of SI base unit "K" 72 | 73 | 74 | 75 | 76 | Exponent of SI base unit "mol" 77 | 78 | 79 | 80 | 81 | Exponent of SI base unit "cd" 82 | 83 | 84 | 85 | 86 | Exponent of SI derived unit "rad" 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | DisplayUnit_value = factor*Unit_value + offset 97 | 98 | 99 | 100 | 101 | Name of DisplayUnit element, e.g. , . "name" must be unique with respect to all other "names" of the DisplayUnit definitions of the same Unit (different Unit elements may have the same DisplayUnit names). 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | Name of Unit element, e.g. "N.m", "Nm", "%/s". "name" must be unique will respect to all other elements of the UnitDefinitions list. The variable values of fmi2SetXXX and fmi2GetXXX are with respect to this unit. 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/fmi-2.0/fmi2VariableDependency.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Copyright(c) 2008-2011 MODELISAR consortium, 6 | 2012-2022 Modelica Association Project "FMI". 7 | All rights reserved. 8 | 9 | This schema file is part of the FMI 2.0.4 standard. 10 | 11 | This file is licensed by the copyright holders under the 2-Clause BSD License 12 | (https://opensource.org/licenses/BSD-2-Clause): 13 | 14 | ---------------------------------------------------------------------------- 15 | Redistribution and use in source and binary forms, with or without 16 | modification, are permitted provided that the following conditions are met: 17 | 18 | - Redistributions of source code must retain the above copyright notice, 19 | this list of conditions and the following disclaimer. 20 | 21 | - Redistributions in binary form must reproduce the above copyright notice, 22 | this list of conditions and the following disclaimer in the documentation 23 | and/or other materials provided with the distribution. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 29 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 31 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 32 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 33 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 35 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | ---------------------------------------------------------------------------- 37 | 38 | 39 | 40 | 41 | 42 | 43 | Dependency of scalar Unknown from Knowns 44 | in Continuous-Time and Event Mode (ModelExchange), 45 | and at Communication Points (CoSimulation): 46 | Unknown=f(Known_1, Known_2, ...). 47 | The Knowns are "inputs", "continuous states" and 48 | "independent variable" (usually time)". 49 | 50 | 51 | 52 | 53 | ScalarVariable index of Unknown 54 | 55 | 56 | 57 | 58 | Defines the dependency of the Unknown (directly or indirectly via auxiliary variables) on the Knowns in Continuous-Time and Event Mode (ModelExchange) and at Communication Points (CoSimulation). If not present, it must be assumed that the Unknown depends on all Knowns. If present as empty list, the Unknown depends on none of the Knowns. Otherwise the Unknown depends on the Knowns defined by the given ScalarVariable indices. The indices are ordered according to size, starting with the smallest index. 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | If not present, it must be assumed that the Unknown depends on the Knowns without a particular structure. Otherwise, the corresponding Known v enters the equation as: 67 | = "dependent": no particular structure, f(v) 68 | = "constant" : constant factor, c*v (only for Real variablse) 69 | = "fixed" : fixed factor, p*v (only for Real variables) 70 | = "tunable" : tunable factor, p*v (only for Real variables) 71 | = "discrete" : discrete factor, d*v (only for Real variables) 72 | If "dependenciesKind" is present, "dependencies" must be present and must have the same number of list elements. 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/fmu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/fmu.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/fmu_manipulation_toolbox.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/help.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/icon-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/icon-round.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/icon.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/icon_fmu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/icon_fmu.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/license.txt: -------------------------------------------------------------------------------- 1 | FMPy Remoting Binaries License 2 | ============================== 3 | 4 | The 32-bit remoting binaries (binaries/win64/.dll and 5 | binaries/win64/server.exe) are part of FMU Manipulation Toolbox 6 | and released under the 2-Clause BSD license: 7 | 8 | Copyright 2023 Renault SAS 9 | 10 | The remoting code is written by Nicolas.LAURENT@Renault.com. 11 | 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright 17 | notice, this list of conditions and the following disclaimer. 18 | 19 | * Redistributions in binary form must reproduce the above 20 | copyright notice, this list of conditions and the following disclaimer 21 | in the documentation and/or other materials provided with the 22 | distribution. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/linux32/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/linux32/.gitkeep -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/linux64/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/linux64/.gitkeep -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/mask.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/model.png -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/win32/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/win32/.gitkeep -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/resources/win64/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/fmu_manipulation_toolbox/resources/win64/.gitkeep -------------------------------------------------------------------------------- /fmu_manipulation_toolbox/version.py: -------------------------------------------------------------------------------- 1 | try: 2 | from fmu_manipulation_toolbox.__version__ import __doc__ as __version__ 3 | except ModuleNotFoundError: 4 | __version__ = "0.0.dev0" 5 | 6 | __author__ = "Nicolas.LAURENT@Renault.com" 7 | __copyright__ = "Copyright 2023-2024, Renault SAS" 8 | __license__ = """This code is released under the 2-Clause BSD license. 9 | See https://github.com/grouperenault/fmu_manipulation_toolbox/blob/main/LICENSE.txt""" 10 | -------------------------------------------------------------------------------- /package.py: -------------------------------------------------------------------------------- 1 | from zipfile import ZipFile 2 | import os 3 | import sys 4 | 5 | version = sys.argv[1] 6 | 7 | # create a ZipFile object 8 | base_directory = "build" 9 | zip_filename = f"fmu_manipulation_toolbox-{version}.zip" 10 | 11 | with ZipFile(zip_filename, 'w') as zip_file: 12 | for folder_name, subfolders, filenames in os.walk(base_directory): 13 | for filename in filenames: 14 | file_path = os.path.join(folder_name, filename) 15 | zip_file.write(file_path, file_path[len(base_directory)+1:]) 16 | 17 | print(f"Package {zip_filename} created") 18 | -------------------------------------------------------------------------------- /remoting/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ___ __ __ 2 | # .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | # | _|| | | | | _| -__| | _ || _| || | _ | 4 | # |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | # Copyright 2023 Renault SAS |_____| 6 | # The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | # This code is released under the 2-Clause BSD license. 8 | # 9 | 10 | cmake_minimum_required(VERSION 3.18) 11 | project (remoting) 12 | set(VERSION "1.0") 13 | 14 | set (CMAKE_C_STANDARD 99) 15 | option(BUILD_32 "Build for 32bits target" OFF) 16 | option(BUILD_TESTER "Build tester executbale" OFF) 17 | 18 | if (WIN32) 19 | if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") 20 | set(FMI_PLATFORM win64) 21 | else () 22 | set(FMI_PLATFORM win32) 23 | endif () 24 | else () 25 | set(THREADS_PREFER_PTHREAD_FLAG ON) 26 | find_package(Threads REQUIRED) 27 | 28 | include(CheckFunctionExists) 29 | check_function_exists(semtimedop HAVE_SEMTIMEDOP) 30 | 31 | if (APPLE) 32 | set(FMI_PLATFORM darwin64) 33 | # Compilation in 32 bits mode is no longer supported in MacOS 34 | else () 35 | if (BUILD_32) 36 | set(FMI_PLATFORM linux32) 37 | set(BITNESS "-m32") 38 | else () 39 | set(FMI_PLATFORM linux64) 40 | endif () 41 | endif() 42 | 43 | endif () 44 | 45 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in 46 | ${CMAKE_CURRENT_BINARY_DIR}/config.h) 47 | 48 | message("FMI_PLATFORM: ${FMI_PLATFORM}") 49 | 50 | if (MSVC) 51 | add_compile_definitions(_CRT_SECURE_NO_WARNINGS) 52 | endif () 53 | 54 | # 55 | # shared memory server 56 | # 57 | add_executable(server_sm 58 | communication.c communication.h 59 | process.c process.h 60 | remote.c remote.h 61 | server.c server.h 62 | ${CMAKE_CURRENT_BINARY_DIR}/config.h 63 | ) 64 | 65 | set_target_properties(server_sm PROPERTIES COMPILE_OPTIONS "${BITNESS}" LINK_FLAGS "${BITNESS}") 66 | target_include_directories(server_sm PRIVATE 67 | ${CMAKE_CURRENT_SOURCE_DIR}/../fmi 68 | ${CMAKE_CURRENT_BINARY_DIR}) 69 | 70 | if (UNIX) 71 | target_link_libraries(server_sm ${CMAKE_DL_LIBS}) 72 | if (NOT APPLE) 73 | target_link_libraries(server_sm rt Threads::Threads) 74 | endif() 75 | endif () 76 | 77 | set_target_properties(server_sm PROPERTIES 78 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../fmu_manipulation_toolbox/resources/${FMI_PLATFORM}" 79 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../fmu_manipulation_toolbox/resources/${FMI_PLATFORM}") 80 | 81 | 82 | # 83 | # shared memory client_sm # 84 | # 85 | add_library(client_sm SHARED 86 | client.c client.h 87 | communication.c communication.h 88 | process.c process.h 89 | remote.c remote.h 90 | ${CMAKE_CURRENT_BINARY_DIR}/config.h 91 | ) 92 | 93 | set_target_properties(client_sm PROPERTIES PREFIX "" COMPILE_OPTIONS "${BITNESS}" LINK_FLAGS "${BITNESS}") 94 | 95 | target_include_directories(client_sm PRIVATE 96 | ${CMAKE_CURRENT_SOURCE_DIR}/../fmi 97 | ${CMAKE_CURRENT_BINARY_DIR}) 98 | 99 | if (UNIX AND NOT APPLE) 100 | target_link_libraries(client_sm rt Threads::Threads) 101 | endif() 102 | 103 | set_target_properties(client_sm PROPERTIES 104 | RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../fmu_manipulation_toolbox/resources/${FMI_PLATFORM}" 105 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../fmu_manipulation_toolbox/resources/${FMI_PLATFORM}") 106 | 107 | # 108 | # test_sizeof 109 | # 110 | if (BUILD_TESTER) 111 | add_executable(test_sizeof 112 | ${CMAKE_CURRENT_SOURCE_DIR}/test_sizeof.c 113 | remote.h 114 | ) 115 | 116 | add_custom_command(OUTPUT test_sizeof.c 117 | COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/gen_sizeof.py 118 | DEPENDS gen_sizeof.py 119 | ) 120 | 121 | 122 | add_executable(test_server 123 | ${CMAKE_CURRENT_SOURCE_DIR}/test_server.c 124 | ) 125 | #target_link_libraries(test_server client_sm) 126 | target_include_directories(test_server PRIVATE 127 | ${CMAKE_CURRENT_SOURCE_DIR}/../fmi 128 | ) 129 | 130 | target_include_directories(test_sizeof PRIVATE 131 | ${CMAKE_CURRENT_SOURCE_DIR}/../fmi 132 | ) 133 | 134 | endif () 135 | -------------------------------------------------------------------------------- /remoting/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | ___ __ __ 3 | .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 4 | | _|| | | | | _| -__| | _ || _| || | _ | 5 | |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 6 | |_____| 7 | ``` 8 | 9 | ## Purpose 10 | 11 | Run model contained inside FMU, which is a shared library, into a separate process. 12 | 13 | 14 | ## Implementation 15 | 16 | Current implementation relies on Shared Memory and is available on Windows, Linux and MacOS only. 17 | 18 | ```mermaid 19 | sequenceDiagram 20 | participant env as Simulation Environment 21 | participant client as client.dll 22 | participant server as server.exe 23 | participant dll as model.dll 24 | Note over env,dll: Initialization 25 | env->>client: fmi2Initialize 26 | client->>+server: spawn 27 | server->>-client: acknowledge 28 | client->>server:fmi2Initialize 29 | server->>dll: load 30 | server->>+dll: fmi2Initialize 31 | dll->>-server: fmi2Component (server_t *) 32 | server->>client: status 33 | client->>env: fmi2Component (client_t*) 34 | Note over env,dll: Main loop until fmi2FreeInstance 35 | env->>client: fmi2 function 36 | client->>server: fmi2 function 37 | server->>+dll: fmi2 function 38 | dll->>-server: status 39 | server->>client: status 40 | client->>env: status 41 | ``` 42 | 43 | ### How it's work ? 44 | 45 | Considering win64 FMU, only the `binaries/win64` folder is populated. It contains `model.dll`. 46 | 47 | #### Add remoting win32: simulate 64bits FMU 32 bits OS 48 | 1. Copy `client_sm.dll` (32 bits) as `model.dll` in `binaries/win32` 49 | 2. Copy `server_sm.exe` (64 bits) in `binaries/win64` 50 | 51 | When Simulation Enviroment will use the FMU on 32 bits OS: 52 | 1. it will load `win32/model.dll` (which is a copy of `client_sm.dll`) 53 | 2. which will communicate with `win64/server_exe`. 54 | 3. which will load `win64/model.dll` 55 | 56 | #### Add remoting win64: simulate 32 bits FMU 64 bits OS 57 | 1. Copy `client_sm.dll` (64 bits) as `model.dll` in `binaries/win64` 58 | 3. Copy `server_sm.exe` (32 bits) in `binaries/win32` 59 | 60 | When Simulation Enviroment will use the FMU on 64bits kernel: 61 | 1. it will load `win64/model.dll` (which is a copy of `client_sm.dll`) 62 | 2. which will communicate with `win32/server_exe`. 63 | 3. which will load `win32/model.dll` 64 | 65 | 66 | ## TODO List 67 | 68 | - [X] Unique name for event/memory 69 | - [X] Deep tests for Stings getters/setters 70 | - [X] Perfomances tests (and improvement if needed) 71 | - [X] Support for more fmi2 function 72 | - [ ] Support for fmi3 73 | - [ ] network communication (multi-OS communication) 74 | 75 | 76 | ## LICENSE 77 | 78 | The remoting code is written by Nicolas.LAURENT@Renault. 79 | This code is released under the 2-Clause BSD license: 80 | 81 | Copyright 2023-2024 Renault SAS 82 | 83 | Redistribution and use in source and binary forms, with or without modification, are permitted 84 | provided that the following conditions are met: 85 | 86 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions 87 | and the following disclaimer. 88 | 89 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 90 | and the following disclaimer in the documentation and/or other materials provided with 91 | the distribution. 92 | 93 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS �AS IS� AND ANY EXPRESS OR 94 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 95 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 96 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 97 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 98 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 99 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 100 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 101 | DAMAGE. 102 | -------------------------------------------------------------------------------- /remoting/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | Rem Local build script. Used for debugging purpose. 3 | echo *** 4 | echo *** Compilation 64bits 5 | echo *** 6 | mkdir build-win64 7 | cd build-win64 8 | cmake .. -A x64 9 | cmake --build . --config Release 10 | cd .. 11 | rmdir /s /q build-win64 12 | 13 | 14 | echo *** 15 | echo *** Compilation 32bits 16 | echo *** 17 | mkdir build-win32 18 | cd build-win32 19 | cmake .. -A Win32 20 | cmake --build . --config Release 21 | cd .. 22 | rmdir /s /q build-win32 23 | 24 | echo *** 25 | echo *** DONE 26 | echo *** -------------------------------------------------------------------------------- /remoting/client.h: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #ifndef CLIENT_H 11 | #define CLIENT_H 12 | 13 | #include "communication.h" 14 | #include "process.h" 15 | #include "remote.h" 16 | 17 | /*----------------------------------------------------------------------------- 18 | C L I E N T _ T 19 | -----------------------------------------------------------------------------*/ 20 | 21 | typedef struct { 22 | fmi2ComponentEnvironment environment; 23 | const fmi2CallbackFunctions *functions; 24 | fmi2CallbackLogger logger; 25 | char *instance_name; 26 | int is_debug; 27 | communication_t *communication; 28 | process_handle_t server_handle; 29 | char shared_key[COMMUNICATION_KEY_LEN]; 30 | } client_t; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /remoting/communication.h: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #ifndef COMMUNICATION_H 11 | #define COMMUNICATION_H 12 | 13 | #ifdef WIN32 14 | # include 15 | #else 16 | # include 17 | # include 18 | #endif 19 | 20 | /*----------------------------------------------------------------------------- 21 | C O M M U N I C A T I O N _ E N D P O I N T _ T 22 | -----------------------------------------------------------------------------*/ 23 | 24 | typedef enum { 25 | COMMUNICATION_CLIENT=1, 26 | COMMUNICATION_SERVER=5 27 | } communication_endpoint_t; 28 | 29 | 30 | /*----------------------------------------------------------------------------- 31 | S H M _ H A N D L E _ T 32 | -----------------------------------------------------------------------------*/ 33 | #ifdef WIN32 34 | typedef HANDLE shm_handle_t; 35 | # define SHM_INVALID NULL 36 | #else 37 | typedef int shm_handle_t; 38 | # define SHM_INVALID -1 39 | #endif 40 | 41 | 42 | /*----------------------------------------------------------------------------- 43 | S E M _ H A N D L E _ T 44 | -----------------------------------------------------------------------------*/ 45 | #ifdef WIN32 46 | typedef HANDLE sem_handle_t; 47 | # define SEM_INVALID NULL 48 | #else 49 | typedef int sem_handle_t; 50 | # define SEM_INVALID -1 51 | #endif 52 | 53 | 54 | /*----------------------------------------------------------------------------- 55 | C O M M U N I C A T I O N _ T 56 | -----------------------------------------------------------------------------*/ 57 | #define COMMUNICATION_KEY_LEN 16 58 | #define COMMUNICATION_TIMEOUT_DEFAULT 3000 59 | typedef struct { 60 | communication_endpoint_t endpoint; 61 | char *sem_name_client; 62 | char *sem_name_server; 63 | char *shm_name; 64 | shm_handle_t map_file; 65 | sem_handle_t client_ready; 66 | sem_handle_t server_ready; 67 | size_t data_size; 68 | void *data; 69 | } communication_t; 70 | 71 | 72 | /*----------------------------------------------------------------------------- 73 | P R O T O T Y P E S 74 | -----------------------------------------------------------------------------*/ 75 | 76 | extern void communication_free(communication_t* communication); 77 | extern communication_t *communication_new(const char *prefix, size_t memory_size, communication_endpoint_t endpoint); 78 | extern void communication_client_ready(const communication_t* communication); 79 | extern void communication_waitfor_server(const communication_t* communication); 80 | extern int communication_timedwaitfor_server(const communication_t* communication, int timeout); 81 | extern void communication_waitfor_client(const communication_t* communication); 82 | extern int communication_timedwaitfor_client(const communication_t* communication, int timeout); 83 | extern void communication_server_ready(const communication_t* communication); 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /remoting/config.h.in: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #ifndef CONFIG_H 11 | #define CONFIG_H 12 | 13 | # define REMOTING_VERSION "${VERSION}" 14 | 15 | #cmakedefine HAVE_SEMTIMEDOP 16 | 17 | 18 | #if defined WIN32 19 | # define CONFIG_DIR_SEP "\\" 20 | # define CONFIG_EXE_SUFFIX ".exe" 21 | # define CONFIG_FMI_BIN "win" 22 | #elif defined __APPLE__ 23 | # define CONFIG_DIR_SEP "/" 24 | # define CONFIG_EXE_SUFFIX "" 25 | # define CONFIG_FMI_BIN "darwin" 26 | #elif defined __linux__ 27 | # define CONFIG_DIR_SEP "/" 28 | # define CONFIG_EXE_SUFFIX "" 29 | # define CONFIG_FMI_BIN "linux" 30 | #else 31 | # error "Architecture not supported" 32 | #endif 33 | 34 | #endif -------------------------------------------------------------------------------- /remoting/gen_sizeof.py: -------------------------------------------------------------------------------- 1 | # 2 | # ___ __ __ 3 | # .' _|.--------.--.--.| |_.-----.-----.| | 4 | # | _|| | | || _| _ | _ || | 5 | # |__| |__|__|__|_____||____|_____|_____||__| 6 | # 7 | # 8 | 9 | import sys 10 | import os 11 | 12 | 13 | directory = os.path.dirname(__file__) 14 | with open(os.path.join(directory, "types.txt")) as file: 15 | types = {} 16 | for line in file.readlines(): 17 | if line[-1] == '\n': 18 | line = line[:-1] 19 | print(line) 20 | (t, size) = line.split(',') 21 | 22 | types[t] = size 23 | 24 | 25 | with open(os.path.join(directory, "test_sizeof.c"), "wt") as sys.stdout: 26 | print(""" 27 | #include 28 | #include 29 | #include 30 | #include "remote.h" 31 | 32 | int main(void) { 33 | int error = 0; 34 | """) 35 | 36 | for t in types.keys(): 37 | print(f''' 38 | if (sizeof({t}) != {types[t]}) {{ 39 | printf("%20s: %zd | ERROR: expected %d\\n", "{t}", sizeof({t}), {types[t]}); 40 | error += 1; 41 | }} else {{ 42 | printf("%20s: %zd | OK\\n", "{t}", sizeof({t})); 43 | }} 44 | ''') 45 | print(""" 46 | return error; 47 | } 48 | """) 49 | -------------------------------------------------------------------------------- /remoting/process.c: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #ifdef WIN32 11 | # include 12 | #else 13 | # include 14 | # include 15 | # include 16 | # include 17 | # include 18 | # include 19 | # include 20 | # include 21 | # include 22 | # include 23 | #endif 24 | 25 | #include /* for fmi2Instanciate symbol */ 26 | 27 | #include "communication.h" 28 | #include "process.h" 29 | 30 | 31 | int process_is_alive(process_handle_t handle) { 32 | #ifdef WIN32 33 | return WaitForSingleObject(handle, 0) == WAIT_TIMEOUT; 34 | #else 35 | return ! kill(handle, 0); 36 | #endif 37 | } 38 | 39 | 40 | process_handle_t process_spawn(char *const argv[]) { 41 | process_handle_t handle; 42 | #ifdef WIN32 43 | char cmd[MAX_PATH]; 44 | STARTUPINFO si; 45 | ZeroMemory(&si, sizeof(si)); 46 | si.cb = sizeof(si); 47 | 48 | ZeroMemory(&handle, sizeof(handle)); 49 | 50 | cmd[0] = '\0'; 51 | for (int i = 0; argv[i]; i++) { 52 | strncat(cmd, argv[i], sizeof(cmd) - strlen(cmd)); 53 | strncat(cmd, " ", sizeof(cmd) - strlen(cmd)); 54 | } 55 | PROCESS_INFORMATION pi; 56 | if (CreateProcessA(NULL, // the path 57 | (LPSTR)cmd, // command line 58 | NULL, // process handle not inheritable 59 | NULL, // thread handle not inheritable 60 | FALSE, // set handle inheritance to FALSE 61 | CREATE_NO_WINDOW, // creation flags 62 | NULL, // use parent's environment block 63 | NULL, // use parent's starting directory 64 | &si, // pointer to STARTUPINFO structure 65 | &pi // pointer to PROCESS_INFORMATION structure 66 | )) { 67 | CloseHandle(pi.hThread); 68 | handle = pi.hProcess; 69 | } 70 | #else 71 | switch(handle = fork()) { 72 | case -1: 73 | return -1; 74 | 75 | case 0: 76 | /* CHILD (server) */ 77 | execv(argv[0], argv); 78 | exit(-1); 79 | 80 | default: 81 | /* FATHER (client) */ 82 | /* NOP */ 83 | break; 84 | } 85 | #endif 86 | return handle; /* Reached only by client */ 87 | } 88 | 89 | 90 | unsigned long int process_current_id(void) { 91 | #ifdef WIN32 92 | return GetCurrentProcessId(); 93 | #else 94 | return getpid(); 95 | #endif 96 | } 97 | 98 | 99 | void process_close_handle(process_handle_t handle) { 100 | #ifdef WIN32 101 | CloseHandle(handle); 102 | #else 103 | (void)handle; 104 | #endif 105 | } 106 | 107 | void process_waitfor(process_handle_t handle) { 108 | #ifdef WIN32 109 | WaitForSingleObject(handle, INFINITE); 110 | #else 111 | int reason; 112 | waitpid(handle, &reason, WNOHANG); 113 | #endif 114 | } 115 | -------------------------------------------------------------------------------- /remoting/process.h: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #ifndef PROCESS_H 11 | #define PROCESS_H 12 | 13 | 14 | /*----------------------------------------------------------------------------- 15 | P R O C E S S _ H A N D L E _ T 16 | -----------------------------------------------------------------------------*/ 17 | #ifdef WIN32 18 | #include 19 | typedef HANDLE process_handle_t; 20 | #else 21 | #include 22 | typedef pid_t process_handle_t; 23 | #endif 24 | 25 | #ifndef MAX_PATH 26 | # define MAX_PATH 4096 27 | #endif 28 | 29 | /*----------------------------------------------------------------------------- 30 | P R O T O T Y P E S 31 | -----------------------------------------------------------------------------*/ 32 | 33 | extern int process_is_alive(process_handle_t handle); 34 | extern process_handle_t process_spawn(char *const argv[]); 35 | extern unsigned long int process_current_id(void); 36 | extern void process_close_handle(process_handle_t handle); 37 | void process_waitfor(process_handle_t handle); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /remoting/remote.c: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #include 11 | #if WIN32 12 | # pragma warning(disable: 4996) /* Stop complaining about strdup() */ 13 | #endif 14 | 15 | #include "remote.h" 16 | 17 | void remote_encode_strings(const char* const src[], char* dst, size_t ns) { 18 | char* off = dst; 19 | size_t remaining = REMOTE_ARG_SIZE; 20 | for(size_t i = 0; i < ns; i += 1) { 21 | size_t len = strlen(src[i]) + 1; 22 | strncat(off, src[i], remaining); 23 | off += len; 24 | remaining -= len; 25 | if (remaining <= 0) 26 | break; 27 | } 28 | } 29 | 30 | 31 | void remote_decode_strings(const char* src, const char* dst[], size_t ns) { 32 | const char* off = src; 33 | size_t remaining = REMOTE_ARG_SIZE; 34 | for (size_t i = 0; i < ns; i += 1) { 35 | size_t len = strlen(off) + 1; 36 | dst[i] = strdup(off); 37 | off += len; 38 | remaining -= len; 39 | if (remaining <= 0) 40 | break; 41 | } 42 | } 43 | 44 | 45 | const char *remote_function_name(remote_function_t function) { 46 | 47 | #define CASE(x) case REMOTE_ ## x: return #x 48 | switch (function) { 49 | CASE(fmi2GetTypesPlatform); 50 | CASE(fmi2GetVersion); 51 | CASE(fmi2SetDebugLogging); 52 | CASE(fmi2Instantiate); 53 | CASE(fmi2FreeInstance); 54 | CASE(fmi2SetupExperiment); 55 | CASE(fmi2EnterInitializationMode); 56 | CASE(fmi2ExitInitializationMode); 57 | CASE(fmi2Terminate); 58 | CASE(fmi2Reset); 59 | CASE(fmi2GetReal); 60 | CASE(fmi2GetInteger); 61 | CASE(fmi2GetBoolean); 62 | CASE(fmi2GetString); 63 | CASE(fmi2SetReal); 64 | CASE(fmi2SetInteger); 65 | CASE(fmi2SetBoolean); 66 | CASE(fmi2SetString); 67 | CASE(fmi2GetFMUstate); 68 | CASE(fmi2SetFMUstate); 69 | CASE(fmi2FreeFMUstate); 70 | CASE(fmi2SerializedFMUstateSize); 71 | CASE(fmi2SerializeFMUstate); 72 | CASE(fmi2DeSerializeFMUstate); 73 | CASE(fmi2GetDirectionalDerivative); 74 | 75 | CASE(fmi2EnterEventMode); 76 | CASE(fmi2NewDiscreteStates); 77 | CASE(fmi2EnterContinuousTimeMode); 78 | CASE(fmi2CompletedIntegratorStep); 79 | CASE(fmi2SetTime); 80 | CASE(fmi2SetContinuousStates); 81 | CASE(fmi2GetDerivatives); 82 | CASE(fmi2GetEventIndicators); 83 | CASE(fmi2GetContinuousStates); 84 | CASE(fmi2GetNominalsOfContinuousStates); 85 | 86 | CASE(fmi2SetRealInputDerivatives); 87 | CASE(fmi2GetRealOutputDerivatives); 88 | CASE(fmi2DoStep); 89 | CASE(fmi2CancelStep); 90 | CASE(fmi2GetStatus); 91 | CASE(fmi2GetRealStatus); 92 | CASE(fmi2GetIntegerStatus); 93 | CASE(fmi2GetBooleanStatus); 94 | CASE(fmi2GetStringStatus); 95 | } 96 | return "UNKNOWN"; 97 | } 98 | -------------------------------------------------------------------------------- /remoting/remote.h: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #ifndef REMOTE_H 11 | #define REMOTE_H 12 | 13 | #include 14 | 15 | typedef enum { 16 | REMOTE_fmi2GetTypesPlatform=0, 17 | REMOTE_fmi2GetVersion=1, 18 | REMOTE_fmi2SetDebugLogging=2, 19 | REMOTE_fmi2Instantiate=3, 20 | REMOTE_fmi2FreeInstance=4, 21 | REMOTE_fmi2SetupExperiment=5, 22 | REMOTE_fmi2EnterInitializationMode=6, 23 | REMOTE_fmi2ExitInitializationMode=7, 24 | REMOTE_fmi2Terminate=8, 25 | REMOTE_fmi2Reset=9, 26 | REMOTE_fmi2GetReal=10, 27 | REMOTE_fmi2GetInteger=11, 28 | REMOTE_fmi2GetBoolean=12, 29 | REMOTE_fmi2GetString=13, 30 | REMOTE_fmi2SetReal=14, 31 | REMOTE_fmi2SetInteger=15, 32 | REMOTE_fmi2SetBoolean=16, 33 | REMOTE_fmi2SetString=17, 34 | REMOTE_fmi2GetFMUstate=18, 35 | REMOTE_fmi2SetFMUstate=19, 36 | REMOTE_fmi2FreeFMUstate=20, 37 | REMOTE_fmi2SerializedFMUstateSize=21, 38 | REMOTE_fmi2SerializeFMUstate=22, 39 | REMOTE_fmi2DeSerializeFMUstate=23, 40 | REMOTE_fmi2GetDirectionalDerivative=24, 41 | 42 | REMOTE_fmi2EnterEventMode=25, 43 | REMOTE_fmi2NewDiscreteStates=26, 44 | REMOTE_fmi2EnterContinuousTimeMode=27, 45 | REMOTE_fmi2CompletedIntegratorStep=28, 46 | REMOTE_fmi2SetTime=29, 47 | REMOTE_fmi2SetContinuousStates=30, 48 | REMOTE_fmi2GetDerivatives=31, 49 | REMOTE_fmi2GetEventIndicators=32, 50 | REMOTE_fmi2GetContinuousStates=33, 51 | REMOTE_fmi2GetNominalsOfContinuousStates=34, 52 | 53 | REMOTE_fmi2SetRealInputDerivatives=35, 54 | REMOTE_fmi2GetRealOutputDerivatives=36, 55 | REMOTE_fmi2DoStep=37, 56 | REMOTE_fmi2CancelStep=38, 57 | REMOTE_fmi2GetStatus=39, 58 | REMOTE_fmi2GetRealStatus=40, 59 | REMOTE_fmi2GetIntegerStatus=41, 60 | REMOTE_fmi2GetBooleanStatus=42, 61 | REMOTE_fmi2GetStringStatus=43, 62 | } remote_function_t; 63 | 64 | 65 | /*--------------------------------------------------------------------------------- 66 | R E M O T E _ D A T A _ T 67 | ---------------------------------------------------------------------------------*/ 68 | 69 | #define REMOTE_MESSAGE_SIZE 8192 70 | #define REMOTE_ARG_SIZE 65536 71 | #define REMOTE_MAX_ARG 8 72 | #define REMOTE_DATA_SIZE sizeof(remote_data_t) 73 | 74 | typedef struct { 75 | fmi2Status status; 76 | remote_function_t function; 77 | char data[REMOTE_MAX_ARG * REMOTE_ARG_SIZE]; 78 | char message[REMOTE_MESSAGE_SIZE]; 79 | } remote_data_t; 80 | 81 | typedef unsigned long portable_size_t; 82 | 83 | /*--------------------------------------------------------------------------------- 84 | M A R S H A L L I N G M A C R O S 85 | ---------------------------------------------------------------------------------*/ 86 | 87 | #define REMOTE_ARG_PTR(_data, _n) (_data + _n*REMOTE_ARG_SIZE) 88 | #define REMOTE_CLEAR_ARGS(_data, _n) memset(_data, 0, _n * REMOTE_ARG_SIZE) 89 | #define REMOTE_ENCODE_VAR(_data, _n, _var) memcpy(_data + _n*REMOTE_ARG_SIZE, &_var, sizeof(_var)) 90 | #define REMOTE_ENCODE_PTR(_data, _n, _ptr, _len) memcpy(_data + _n*REMOTE_ARG_SIZE, _ptr, sizeof(*_ptr)*_len) 91 | #define REMOTE_ENCODE_STR(_data, _n, _ptr) strncpy(_data + _n*REMOTE_ARG_SIZE, _ptr, REMOTE_ARG_SIZE) 92 | 93 | #define REMOTE_DECODE_VAR(_data, _n, _type) (*((_type *)(_data + _n*REMOTE_ARG_SIZE))) 94 | #define REMOTE_DECODE_PTR(_data, _n, _type) ((_type)(_data + _n*REMOTE_ARG_SIZE)) 95 | #define REMOTE_DECODE_STR(_data, _n) REMOTE_DECODE_PTR(_data, _n, fmi2String) 96 | 97 | 98 | /*----------------------------------------------------------------------------- 99 | P R O T O T Y P E S 100 | -----------------------------------------------------------------------------*/ 101 | 102 | extern void remote_clear_args(int n); 103 | extern void remote_encode_strings(const char *const src[], char* dst, size_t ns); 104 | extern void remote_decode_strings(const char* src, const char* dst[], size_t ns); 105 | extern const char* remote_function_name(remote_function_t function); 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /remoting/server.h: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #ifndef SERVER_H 11 | #define SERVER_H 12 | 13 | #include 14 | 15 | #include "communication.h" 16 | #include "process.h" 17 | #include "remote.h" 18 | 19 | /*---------------------------------------------------------------------------- 20 | F M U _ L I B R A R Y T 21 | ----------------------------------------------------------------------------*/ 22 | 23 | #ifdef WIN32 24 | typedef HINSTANCE library_t; 25 | #else 26 | typedef void* library_t; 27 | #endif 28 | 29 | 30 | /*---------------------------------------------------------------------------- 31 | F M U _ E N T R I E S _ T 32 | ----------------------------------------------------------------------------*/ 33 | /* 34 | * List all interesting entry points of a shared object (DLL) 35 | * It may contain multiple versions of FMI API. 36 | */ 37 | #define DECLARE_FMI_CALLBACK(x) x ## TYPE *x 38 | typedef struct { 39 | /* FMI2 functions */ 40 | DECLARE_FMI_CALLBACK(fmi2GetTypesPlatform); 41 | DECLARE_FMI_CALLBACK(fmi2GetVersion); 42 | DECLARE_FMI_CALLBACK(fmi2SetDebugLogging); 43 | DECLARE_FMI_CALLBACK(fmi2Instantiate); 44 | DECLARE_FMI_CALLBACK(fmi2FreeInstance); 45 | DECLARE_FMI_CALLBACK(fmi2SetupExperiment); 46 | DECLARE_FMI_CALLBACK(fmi2EnterInitializationMode); 47 | DECLARE_FMI_CALLBACK(fmi2ExitInitializationMode); 48 | DECLARE_FMI_CALLBACK(fmi2Terminate); 49 | DECLARE_FMI_CALLBACK(fmi2Reset); 50 | DECLARE_FMI_CALLBACK(fmi2GetReal); 51 | DECLARE_FMI_CALLBACK(fmi2GetInteger); 52 | DECLARE_FMI_CALLBACK(fmi2GetBoolean); 53 | DECLARE_FMI_CALLBACK(fmi2GetString); 54 | DECLARE_FMI_CALLBACK(fmi2SetReal); 55 | DECLARE_FMI_CALLBACK(fmi2SetInteger); 56 | DECLARE_FMI_CALLBACK(fmi2SetBoolean); 57 | DECLARE_FMI_CALLBACK(fmi2SetString); 58 | DECLARE_FMI_CALLBACK(fmi2GetFMUstate); 59 | DECLARE_FMI_CALLBACK(fmi2SetFMUstate); 60 | DECLARE_FMI_CALLBACK(fmi2FreeFMUstate); 61 | DECLARE_FMI_CALLBACK(fmi2SerializedFMUstateSize); 62 | DECLARE_FMI_CALLBACK(fmi2SerializeFMUstate); 63 | DECLARE_FMI_CALLBACK(fmi2DeSerializeFMUstate); 64 | DECLARE_FMI_CALLBACK(fmi2GetDirectionalDerivative); 65 | DECLARE_FMI_CALLBACK(fmi2SetRealInputDerivatives); 66 | DECLARE_FMI_CALLBACK(fmi2GetRealOutputDerivatives); 67 | DECLARE_FMI_CALLBACK(fmi2DoStep); 68 | DECLARE_FMI_CALLBACK(fmi2CancelStep); 69 | DECLARE_FMI_CALLBACK(fmi2GetStatus); 70 | DECLARE_FMI_CALLBACK(fmi2GetRealStatus); 71 | DECLARE_FMI_CALLBACK(fmi2GetIntegerStatus); 72 | DECLARE_FMI_CALLBACK(fmi2GetBooleanStatus); 73 | DECLARE_FMI_CALLBACK(fmi2GetStringStatus); 74 | 75 | DECLARE_FMI_CALLBACK(fmi2EnterEventMode); 76 | DECLARE_FMI_CALLBACK(fmi2NewDiscreteStates); 77 | DECLARE_FMI_CALLBACK(fmi2EnterContinuousTimeMode); 78 | DECLARE_FMI_CALLBACK(fmi2GetNominalsOfContinuousStates); 79 | DECLARE_FMI_CALLBACK(fmi2GetDerivatives); 80 | DECLARE_FMI_CALLBACK(fmi2GetContinuousStates); 81 | DECLARE_FMI_CALLBACK(fmi2CompletedIntegratorStep); 82 | DECLARE_FMI_CALLBACK(fmi2SetTime); 83 | DECLARE_FMI_CALLBACK(fmi2SetContinuousStates); 84 | DECLARE_FMI_CALLBACK(fmi2GetEventIndicators); 85 | } fmu_entries_t; 86 | #undef DECLARE_FMI_CALLBACK 87 | 88 | 89 | /*----------------------------------------------------------------------------- 90 | S E R V E R _ T 91 | -----------------------------------------------------------------------------*/ 92 | 93 | typedef struct { 94 | communication_t *communication; 95 | fmu_entries_t entries; 96 | library_t library; 97 | const char *library_filename; 98 | fmi2Component component; 99 | char *instance_name; 100 | fmi2CallbackFunctions functions; 101 | int is_debug; 102 | process_handle_t parent_handle; 103 | char shared_key[COMMUNICATION_KEY_LEN]; 104 | } server_t; 105 | 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /remoting/test_server.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void logger(fmi2ComponentEnvironment componentEnvironment, 8 | fmi2String instanceName, fmi2Status status, fmi2String category, 9 | fmi2String message, ...) { 10 | 11 | va_list ap; 12 | 13 | printf("instance=%s status=%d category=%s | ", instanceName, status, category); 14 | va_start(ap, message); 15 | vprintf(message, ap); 16 | va_end(ap); 17 | printf("\n"); 18 | 19 | return; 20 | } 21 | 22 | 23 | int main(int argc, char **argv) { 24 | 25 | void *lib=dlopen(argv[1], RTLD_LAZY); 26 | if (! lib) { 27 | printf("Cannot open %s: %s\n", argv[1], dlerror()); 28 | return -1; 29 | } 30 | 31 | fmi2InstantiateTYPE *instantiate = dlsym(lib, "fmi2Instantiate"); 32 | if (! instantiate) { 33 | printf("Cannot find fmi2Instantiate\n"); 34 | return -2; 35 | } 36 | 37 | fmi2CallbackFunctions function; 38 | 39 | function.logger = logger; 40 | function.allocateMemory = calloc; 41 | function.freeMemory = free; 42 | function.stepFinished = NULL; 43 | function.componentEnvironment = NULL; 44 | 45 | void *ptr = instantiate("my_instance", fmi2CoSimulation, "{8c4e810f-3da3-4a00-8276-176fa3c9f000}", "fmuResourceLocation", 46 | &function, fmi2True, fmi2True); 47 | 48 | printf("PTR = %p\n", ptr); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /remoting/test_sizeof.c: -------------------------------------------------------------------------------- 1 | /* ___ __ __ 2 | * .' _|.--------.--.--. .----.-----.--------.-----.| |_|__|.-----.-----. 3 | * | _|| | | | | _| -__| | _ || _| || | _ | 4 | * |__| |__|__|__|_____| |__| |_____|__|__|__|_____||____|__||__|__|___ | 5 | * Copyright 2023 Renault SAS |_____| 6 | * The remoting code is written by Nicolas.LAURENT@Renault.com. 7 | * This code is released under the 2-Clause BSD license. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include "remote.h" 14 | 15 | int main(void) { 16 | int error = 0; 17 | 18 | 19 | if (sizeof(fmi2Boolean) != 4) { 20 | printf("%20s: %zd | ERROR: expected %d\n", "fmi2Boolean", sizeof(fmi2Boolean), 4); 21 | error += 1; 22 | } else { 23 | printf("%20s: %zd | OK\n", "fmi2Boolean", sizeof(fmi2Boolean)); 24 | } 25 | 26 | 27 | if (sizeof(fmi2Byte) != 1) { 28 | printf("%20s: %zd | ERROR: expected %d\n", "fmi2Byte", sizeof(fmi2Byte), 1); 29 | error += 1; 30 | } else { 31 | printf("%20s: %zd | OK\n", "fmi2Byte", sizeof(fmi2Byte)); 32 | } 33 | 34 | 35 | if (sizeof(fmi2Integer) != 4) { 36 | printf("%20s: %zd | ERROR: expected %d\n", "fmi2Integer", sizeof(fmi2Integer), 4); 37 | error += 1; 38 | } else { 39 | printf("%20s: %zd | OK\n", "fmi2Integer", sizeof(fmi2Integer)); 40 | } 41 | 42 | 43 | if (sizeof(fmi2Real) != 8) { 44 | printf("%20s: %zd | ERROR: expected %d\n", "fmi2Real", sizeof(fmi2Real), 8); 45 | error += 1; 46 | } else { 47 | printf("%20s: %zd | OK\n", "fmi2Real", sizeof(fmi2Real)); 48 | } 49 | 50 | 51 | if (sizeof(fmi2StatusKind) != 4) { 52 | printf("%20s: %zd | ERROR: expected %d\n", "fmi2StatusKind", sizeof(fmi2StatusKind), 4); 53 | error += 1; 54 | } else { 55 | printf("%20s: %zd | OK\n", "fmi2StatusKind", sizeof(fmi2StatusKind)); 56 | } 57 | 58 | 59 | if (sizeof(fmi2Type) != 4) { 60 | printf("%20s: %zd | ERROR: expected %d\n", "fmi2Type", sizeof(fmi2Type), 4); 61 | error += 1; 62 | } else { 63 | printf("%20s: %zd | OK\n", "fmi2Type", sizeof(fmi2Type)); 64 | } 65 | 66 | 67 | if (sizeof(fmi2ValueReference) != 4) { 68 | printf("%20s: %zd | ERROR: expected %d\n", "fmi2ValueReference", sizeof(fmi2ValueReference), 4); 69 | error += 1; 70 | } else { 71 | printf("%20s: %zd | OK\n", "fmi2ValueReference", sizeof(fmi2ValueReference)); 72 | } 73 | 74 | 75 | if (sizeof(portable_size_t) != 4) { 76 | printf("%20s: %zd | ERROR: expected %d\n", "portable_size_t", sizeof(portable_size_t), 4); 77 | error += 1; 78 | } else { 79 | printf("%20s: %zd | OK\n", "portable_size_t", sizeof(portable_size_t)); 80 | } 81 | 82 | 83 | return error; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /remoting/types.txt: -------------------------------------------------------------------------------- 1 | fmi2Boolean,4 2 | fmi2Byte,1 3 | fmi2Integer,4 4 | fmi2Real,8 5 | fmi2StatusKind,4 6 | fmi2Type,4 7 | fmi2ValueReference,4 8 | portable_size_t,4 9 | -------------------------------------------------------------------------------- /remoting/version.h: -------------------------------------------------------------------------------- 1 | #define REMOTING_VERSION "LOCAL" 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PySide6 >= 6.8.0 2 | xmlschema >= 3.3.1 3 | elementpath >= 4.4.0 4 | colorama >= 0.4.6 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [build] 2 | build_scripts=build/scripts 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from setuptools import setup 4 | 5 | from fmu_manipulation_toolbox.version import __author__ as author, __version__ as default_version 6 | 7 | try: 8 | version = os.environ["GITHUB_REF_NAME"] 9 | except Exception as e: 10 | print(f"Cannot get repository status: {e}. Defaulting to {default_version}") 11 | version = default_version 12 | 13 | if not re.match(r"[A-Za-z]?\d+(\.\d)+", version): 14 | print(f"WARNING: Version {version} does not match standard. The publication will fail !") 15 | version = default_version 16 | 17 | # Create __version__.py 18 | try: 19 | with open("fmu_manipulation_toolbox/__version__.py", "wt") as file: 20 | print(f"'{version}'", file=file) 21 | except Exception as e: 22 | print(f"Cannot create __version__.py: {e}") 23 | 24 | setup( 25 | name="fmu_manipulation_toolbox", 26 | version=version, 27 | packages=["fmu_manipulation_toolbox"], 28 | package_data={"fmu_manipulation_toolbox": [ 29 | "resources/win32/client_sm.dll", 30 | "resources/win32/server_sm.exe", 31 | "resources/win64/client_sm.dll", 32 | "resources/win64/server_sm.exe", 33 | "resources/win64/container.dll", 34 | "resources/linux64/client_sm.so", 35 | "resources/linux64/server_sm", 36 | "resources/linux64/container.so", 37 | "resources/linux32/client_sm.so", 38 | "resources/linux32/server_sm", 39 | "resources/license.txt", 40 | "resources/*.png", 41 | "resources/fmi-2.0/*.xsd", 42 | ]}, 43 | entry_points={"console_scripts": ["fmutool = fmu_manipulation_toolbox.__main__:main", 44 | "fmucontainer = fmu_manipulation_toolbox.cli:fmucontainer"], 45 | }, 46 | author=author, 47 | url="https://github.com/grouperenault/fmu_manipulation_toolbox/", 48 | description="FMU Manipulation Toolbox is a python application for modifying Functional Mock-up Units " 49 | "(FMUs) without recompilation or bundling them into FMU Containers", 50 | long_description="""FMU Manipulation Toolbox is a python application for modifying Functional Mock-up Units (FMUs) 51 | without recompilation. It mainly modifies the `modelDescription.xml` file. It is highly customizable. 52 | 53 | Manipulating the `modelDescription.xml` can be a dangerous thing! Communicating with the FMU-developer and adapting 54 | the way the FMU is generated, is the preferable when possible. 55 | 56 | FMU Manipulation Toolbox also allows to group FMU's inside FMU Containers. 57 | """, 58 | install_requires=[ 59 | "PySide6 >= 6.8.0", 60 | "xmlschema >= 3.3.1", 61 | "elementpath >= 4.4.0", 62 | "colorama >= 0.4.6", 63 | ], 64 | license="BSD-2-Clause", 65 | python_requires=">=3.8", 66 | ) 67 | 68 | os.remove("fmu_manipulation_toolbox/__version__.py") 69 | -------------------------------------------------------------------------------- /tests/containers/arch/REF-flat-dump.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flat.fmu", 3 | "mt": false, 4 | "profiling": false, 5 | "auto_link": true, 6 | "auto_input": true, 7 | "auto_output": true, 8 | "auto_parameter": false, 9 | "auto_local": false, 10 | "step_size": 0.5, 11 | "fmu": [ 12 | "gain.fmu", 13 | "integrate.fmu", 14 | "sine.fmu" 15 | ], 16 | "output": [ 17 | [ 18 | "gain.fmu", 19 | "final", 20 | "final" 21 | ] 22 | ], 23 | "link": [ 24 | [ 25 | "sine.fmu", 26 | "sine", 27 | "integrate.fmu", 28 | "sine" 29 | ], 30 | [ 31 | "integrate.fmu", 32 | "integrate", 33 | "gain.fmu", 34 | "integrate" 35 | ] 36 | ] 37 | } -------------------------------------------------------------------------------- /tests/containers/arch/REF-hierarchical-dump.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hierarchical.fmu", 3 | "mt": false, 4 | "profiling": false, 5 | "auto_link": true, 6 | "auto_input": true, 7 | "auto_output": true, 8 | "auto_parameter": false, 9 | "auto_local": false, 10 | "step_size": 0.5, 11 | "container": [ 12 | { 13 | "name": "level1.fmu", 14 | "mt": false, 15 | "profiling": false, 16 | "auto_link": true, 17 | "auto_input": true, 18 | "auto_output": true, 19 | "auto_parameter": false, 20 | "auto_local": false, 21 | "step_size": 0.01, 22 | "fmu": [ 23 | "integrate.fmu", 24 | "sine.fmu" 25 | ], 26 | "output": [ 27 | [ 28 | "integrate.fmu", 29 | "integrate", 30 | "integrate" 31 | ] 32 | ], 33 | "link": [ 34 | [ 35 | "sine.fmu", 36 | "sine", 37 | "integrate.fmu", 38 | "sine" 39 | ] 40 | ] 41 | } 42 | ], 43 | "fmu": [ 44 | "gain.fmu", 45 | "level1.fmu" 46 | ], 47 | "output": [ 48 | [ 49 | "gain.fmu", 50 | "final", 51 | "final" 52 | ] 53 | ], 54 | "link": [ 55 | [ 56 | "level1.fmu", 57 | "integrate", 58 | "gain.fmu", 59 | "integrate" 60 | ] 61 | ] 62 | } -------------------------------------------------------------------------------- /tests/containers/arch/REF-reversed-dump.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reversed.fmu", 3 | "mt": false, 4 | "profiling": false, 5 | "auto_link": true, 6 | "auto_input": true, 7 | "auto_output": true, 8 | "auto_parameter": false, 9 | "auto_local": false, 10 | "step_size": 0.01, 11 | "container": [ 12 | { 13 | "name": "level1.fmu", 14 | "mt": false, 15 | "profiling": false, 16 | "auto_link": true, 17 | "auto_input": true, 18 | "auto_output": true, 19 | "auto_parameter": false, 20 | "auto_local": false, 21 | "step_size": 0.5, 22 | "fmu": [ 23 | "gain.fmu" 24 | ], 25 | "input": [ 26 | [ 27 | "integrate", 28 | "gain.fmu", 29 | "integrate" 30 | ] 31 | ], 32 | "output": [ 33 | [ 34 | "gain.fmu", 35 | "final", 36 | "final" 37 | ] 38 | ] 39 | } 40 | ], 41 | "fmu": [ 42 | "integrate.fmu", 43 | "level1.fmu", 44 | "sine.fmu" 45 | ], 46 | "output": [ 47 | [ 48 | "level1.fmu", 49 | "final", 50 | "final" 51 | ] 52 | ], 53 | "link": [ 54 | [ 55 | "sine.fmu", 56 | "sine", 57 | "integrate.fmu", 58 | "sine" 59 | ], 60 | [ 61 | "integrate.fmu", 62 | "integrate", 63 | "level1.fmu", 64 | "integrate" 65 | ] 66 | ] 67 | } -------------------------------------------------------------------------------- /tests/containers/arch/flat.json: -------------------------------------------------------------------------------- 1 | { 2 | "fmu": [ 3 | "sine.fmu", 4 | "integrate.fmu", 5 | "gain.fmu" 6 | ], 7 | "step_size": 0.5 8 | } 9 | -------------------------------------------------------------------------------- /tests/containers/arch/gain.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/containers/arch/gain.fmu -------------------------------------------------------------------------------- /tests/containers/arch/hierarchical.json: -------------------------------------------------------------------------------- 1 | { 2 | "container": [ 3 | { 4 | "name": "level1.fmu", 5 | "fmu": [ 6 | "sine.fmu", 7 | "integrate.fmu" 8 | ], 9 | "step_size": 0.01 10 | } 11 | ], 12 | "fmu": [ 13 | "gain.fmu" 14 | ], 15 | "step_size": 0.5 16 | } -------------------------------------------------------------------------------- /tests/containers/arch/integrate.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/containers/arch/integrate.fmu -------------------------------------------------------------------------------- /tests/containers/arch/nested.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "level0.fmu", 3 | "fmu": [ 4 | "fmu0a.fmu", 5 | "fmu0b.fmu" 6 | ], 7 | "container": [ 8 | { 9 | "name": "level1.fmu", 10 | "fmu": [ 11 | "fmu1a.fmu", 12 | "fmu1b.fmu", 13 | "fmu1c.fmu" 14 | ], 15 | "link": [ 16 | ["fmu1a.fmu", "fmu1a_out0", "fmu1b.fmu", "fmu1b_in1"], 17 | ["level2a.fmu", "level2a_out0", "fmu1b.fmu", "fmu1b_in2"], 18 | ["fmu1b.fmu", "fmu1b_out1", "fmu1c.fmu", "fmu1c_in0"], 19 | ["fmu1b.fmu", "fmu1b_out2", "level2b.fmu", "level2b_in0"] 20 | ], 21 | "input": [ 22 | ["level1_in0", "fmu1b.fmu", "fmu1b_in0"], 23 | ["level1_in1", "fmu1a.fmu", "fmu1a_in0"], 24 | ["level1_in2", "level2a.fmu", "level2a_in0"] 25 | ], 26 | "output": [ 27 | [ "fmu1b.fmu", "fmu1b_out0", "level1_out0"], 28 | [ "fmu1c.fmu", "fmu1c_out0", "level1_out1"], 29 | [ "level2b.fmu", "level2b_out0", "level1_out2"] 30 | ], 31 | "container": [ 32 | { 33 | "name": "level2a.fmu", 34 | "fmu": [ 35 | "fmu2a.fmu" 36 | ], 37 | "input": [ 38 | ["level2a_in0", "fmu2a.fmu", "fmu2a_in0"] 39 | ], 40 | "output": [ 41 | ["fmu2a.fmu", "fmu2a_out0", "level2a_out0"] 42 | ] 43 | }, 44 | { 45 | "name": "level2b.fmu", 46 | "fmu": [ 47 | "fmu2b.fmu" 48 | ], 49 | "input": [ 50 | ["level2b_in0", "fmu2b.fmu", "fmu2b_in0"] 51 | ], 52 | "output": [ 53 | ["fmu2b.fmu", "fmu2b_out0", "level2b_out0"] 54 | ] 55 | } 56 | ] 57 | } 58 | ], 59 | "input": [ 60 | ["IN_A", "level1.fmu", "level1_in0"], 61 | ["IN_B", "fmu0a.fmu", "fmu0a_in0"] 62 | ], 63 | "output": [ 64 | ["level1.fmu", "level1_out0", "OUT_A"], 65 | ["fmu0b.fmu", "fmu0b_out0", "OUT_B"] 66 | ], 67 | "link": [ 68 | ["fmu0a.fmu", "fmu0a_out0", "level1.fmu", "level1_in1"], 69 | ["fmu0a.fmu", "fmu0a_out1", "level1.fmu", "level1_in2"], 70 | ["level1.fmu", "level1_out1", "fmu0b.fmu", "fmu0b_in0"], 71 | ["level1.fmu", "level1_out2", "fmu0b.fmu", "fmu0b_in1"] 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /tests/containers/arch/reversed.json: -------------------------------------------------------------------------------- 1 | { 2 | "fmu": [ 3 | "sine.fmu", 4 | "integrate.fmu" 5 | ], 6 | "step_size": 0.01, 7 | "container": [ 8 | { 9 | "name": "level1.fmu", 10 | "fmu": [ 11 | "gain.fmu" 12 | ], 13 | "step_size": 0.5 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tests/containers/arch/sine.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/containers/arch/sine.fmu -------------------------------------------------------------------------------- /tests/containers/arch/subdir/gain2.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/containers/arch/subdir/gain2.fmu -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/REF-bouncing-profiling.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bouncing-profiling.fmu", 3 | "mt": false, 4 | "profiling": true, 5 | "auto_link": true, 6 | "auto_input": true, 7 | "auto_output": true, 8 | "auto_parameter": false, 9 | "auto_local": false, 10 | "fmu": [ 11 | "bb_position.fmu", 12 | "bb_velocity.fmu" 13 | ], 14 | "output": [ 15 | [ 16 | "bb_position.fmu", 17 | "position1", 18 | "position" 19 | ], 20 | [ 21 | "bb_velocity.fmu", 22 | "velocity", 23 | "velocity" 24 | ] 25 | ], 26 | "link": [ 27 | [ 28 | "bb_position.fmu", 29 | "is_ground", 30 | "bb_velocity.fmu", 31 | "reset" 32 | ], 33 | [ 34 | "bb_velocity.fmu", 35 | "velocity", 36 | "bb_position.fmu", 37 | "velocity" 38 | ] 39 | ] 40 | } -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/REF-bouncing.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bouncing.fmu", 3 | "mt": true, 4 | "profiling": false, 5 | "auto_link": true, 6 | "auto_input": true, 7 | "auto_output": true, 8 | "auto_parameter": false, 9 | "auto_local": false, 10 | "fmu": [ 11 | "bb_position.fmu", 12 | "bb_velocity.fmu" 13 | ], 14 | "output": [ 15 | [ 16 | "bb_position.fmu", 17 | "position1", 18 | "position" 19 | ], 20 | [ 21 | "bb_velocity.fmu", 22 | "velocity", 23 | "velocity" 24 | ] 25 | ], 26 | "link": [ 27 | [ 28 | "bb_position.fmu", 29 | "is_ground", 30 | "bb_velocity.fmu", 31 | "reset" 32 | ], 33 | [ 34 | "bb_velocity.fmu", 35 | "velocity", 36 | "bb_position.fmu", 37 | "velocity" 38 | ] 39 | ] 40 | } -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/REF-container-profiling.txt: -------------------------------------------------------------------------------- 1 | # Don't use MT 2 | 0 3 | # Profiling ENABLED 4 | 1 5 | # Internal time step in seconds 6 | 0.001 7 | # NB of embedded FMU's 8 | 2 9 | bb_position.fmu 10 | bb_position 11 | {8fbd9f16-ceaa-97ed-127f-987a60b25648} 12 | bb_velocity.fmu 13 | bb_velocity 14 | {abf5f61d-b459-3641-3a2c-1e594b990280} 15 | # NB local variables Real, Integer, Boolean, String 16 | 3 0 1 0 17 | # CONTAINER I/O: [ ] 18 | # Real 19 | 5 5 20 | 0 1 -2 0 21 | 1 1 -2 1 22 | 3 1 0 1 23 | 4 1 1 0 24 | 2 1 -1 2 25 | # Integer 26 | 0 0 27 | # Boolean 28 | 1 1 29 | 0 1 -1 0 30 | # String 31 | 0 0 32 | # Inputs of bb_position.fmu - Real: 33 | 1 34 | 2 0 35 | # Inputs of bb_position.fmu - Integer: 36 | 0 37 | # Inputs of bb_position.fmu - Boolean: 38 | 0 39 | # Inputs of bb_position.fmu - String: 40 | 0 41 | # Start values of bb_position.fmu - Real: 42 | 0 43 | # Start values of bb_position.fmu - Integer: 44 | 0 45 | # Start values of bb_position.fmu - Boolean: 46 | 0 47 | # Start values of bb_position.fmu - String: 48 | 0 49 | # Outputs of bb_position.fmu - Real: 50 | 0 51 | # Outputs of bb_position.fmu - Integer: 52 | 0 53 | # Outputs of bb_position.fmu - Boolean: 54 | 1 55 | 0 0 56 | # Outputs of bb_position.fmu - String: 57 | 0 58 | # Inputs of bb_velocity.fmu - Real: 59 | 0 60 | # Inputs of bb_velocity.fmu - Integer: 61 | 0 62 | # Inputs of bb_velocity.fmu - Boolean: 63 | 1 64 | 0 0 65 | # Inputs of bb_velocity.fmu - String: 66 | 0 67 | # Start values of bb_velocity.fmu - Real: 68 | 0 69 | # Start values of bb_velocity.fmu - Integer: 70 | 0 71 | # Start values of bb_velocity.fmu - Boolean: 72 | 0 73 | # Start values of bb_velocity.fmu - String: 74 | 0 75 | # Outputs of bb_velocity.fmu - Real: 76 | 1 77 | 2 0 78 | # Outputs of bb_velocity.fmu - Integer: 79 | 0 80 | # Outputs of bb_velocity.fmu - Boolean: 81 | 0 82 | # Outputs of bb_velocity.fmu - String: 83 | 0 84 | -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/REF-container.txt: -------------------------------------------------------------------------------- 1 | # Use MT 2 | 1 3 | # Profiling DISABLED 4 | 0 5 | # Internal time step in seconds 6 | 0.001 7 | # NB of embedded FMU's 8 | 2 9 | bb_position.fmu 10 | bb_position 11 | {8fbd9f16-ceaa-97ed-127f-987a60b25648} 12 | bb_velocity.fmu 13 | bb_velocity 14 | {abf5f61d-b459-3641-3a2c-1e594b990280} 15 | # NB local variables Real, Integer, Boolean, String 16 | 1 0 1 0 17 | # CONTAINER I/O: [ ] 18 | # Real 19 | 3 3 20 | 1 1 0 1 21 | 2 1 1 0 22 | 0 1 -1 0 23 | # Integer 24 | 0 0 25 | # Boolean 26 | 1 1 27 | 0 1 -1 0 28 | # String 29 | 0 0 30 | # Inputs of bb_position.fmu - Real: 31 | 1 32 | 0 0 33 | # Inputs of bb_position.fmu - Integer: 34 | 0 35 | # Inputs of bb_position.fmu - Boolean: 36 | 0 37 | # Inputs of bb_position.fmu - String: 38 | 0 39 | # Start values of bb_position.fmu - Real: 40 | 0 41 | # Start values of bb_position.fmu - Integer: 42 | 0 43 | # Start values of bb_position.fmu - Boolean: 44 | 0 45 | # Start values of bb_position.fmu - String: 46 | 0 47 | # Outputs of bb_position.fmu - Real: 48 | 0 49 | # Outputs of bb_position.fmu - Integer: 50 | 0 51 | # Outputs of bb_position.fmu - Boolean: 52 | 1 53 | 0 0 54 | # Outputs of bb_position.fmu - String: 55 | 0 56 | # Inputs of bb_velocity.fmu - Real: 57 | 0 58 | # Inputs of bb_velocity.fmu - Integer: 59 | 0 60 | # Inputs of bb_velocity.fmu - Boolean: 61 | 1 62 | 0 0 63 | # Inputs of bb_velocity.fmu - String: 64 | 0 65 | # Start values of bb_velocity.fmu - Real: 66 | 0 67 | # Start values of bb_velocity.fmu - Integer: 68 | 0 69 | # Start values of bb_velocity.fmu - Boolean: 70 | 0 71 | # Start values of bb_velocity.fmu - String: 72 | 0 73 | # Outputs of bb_velocity.fmu - Real: 74 | 1 75 | 0 0 76 | # Outputs of bb_velocity.fmu - Integer: 77 | 0 78 | # Outputs of bb_velocity.fmu - Boolean: 79 | 0 80 | # Outputs of bb_velocity.fmu - String: 81 | 0 82 | -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/REF-modelDescription-profiling.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/bb_position.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/containers/bouncing_ball/bb_position.fmu -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/bb_velocity.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/containers/bouncing_ball/bb_velocity.fmu -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/bouncing-profiling.csv: -------------------------------------------------------------------------------- 1 | rule;from_fmu;from_port;to_fmu;to_port 2 | FMU;bb_position.fmu;;; 3 | FMU;bb_velocity.fmu;;; 4 | OUTPUT;bb_position.fmu;position1;;position 5 | LINK;bb_position.fmu;is_ground;bb_velocity.fmu;reset 6 | LINK;bb_velocity.fmu;velocity;bb_position.fmu;velocity 7 | OUTPUT;bb_velocity.fmu;velocity;; 8 | -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/bouncing.csv: -------------------------------------------------------------------------------- 1 | rule;from_fmu;from_port;to_fmu;to_port 2 | FMU;bb_position.fmu;;; 3 | FMU;bb_velocity.fmu;;; 4 | OUTPUT;bb_position.fmu;position1;;position 5 | LINK;bb_position.fmu;is_ground;bb_velocity.fmu;reset 6 | LINK;bb_velocity.fmu;velocity;bb_position.fmu;velocity 7 | OUTPUT;bb_velocity.fmu;velocity;; 8 | -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/bouncing_auto.csv: -------------------------------------------------------------------------------- 1 | rule;from_fmu;from_port;to_fmu;to_port 2 | FMU;bb_position.fmu;;; 3 | FMU;bb_velocity.fmu;;; 4 | LINK;bb_position.fmu;velocity;bb_velocity.fmu;reset 5 | 6 | -------------------------------------------------------------------------------- /tests/containers/bouncing_ball/bouncing_unlinked.csv: -------------------------------------------------------------------------------- 1 | rule;from_fmu;from_port;to_fmu;to_port 2 | FMU;bb_position.fmu;;; 3 | FMU;bb_velocity.fmu;;; 4 | 5 | -------------------------------------------------------------------------------- /tests/containers/ssp/REF-bouncing-dump.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bouncing.fmu", 3 | "mt": false, 4 | "profiling": false, 5 | "auto_link": false, 6 | "auto_input": false, 7 | "auto_output": false, 8 | "auto_parameter": false, 9 | "auto_local": false, 10 | "fmu": [ 11 | "resources/bb_position.fmu", 12 | "resources/bb_velocity.fmu" 13 | ], 14 | "link": [ 15 | [ 16 | "resources/bb_velocity.fmu", 17 | "velocity", 18 | "resources/bb_position.fmu", 19 | "velocity" 20 | ], 21 | [ 22 | "resources/bb_position.fmu", 23 | "is_ground", 24 | "resources/bb_velocity.fmu", 25 | "reset" 26 | ] 27 | ] 28 | } -------------------------------------------------------------------------------- /tests/containers/ssp/bouncing.ssp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/containers/ssp/bouncing.ssp -------------------------------------------------------------------------------- /tests/containers/start/REF-container.txt: -------------------------------------------------------------------------------- 1 | # Don't use MT 2 | 0 3 | # Profiling DISABLED 4 | 0 5 | # Internal time step in seconds 6 | 0.1 7 | # NB of embedded FMU's 8 | 1 9 | slx.fmu 10 | G 11 | {6853134c-427d-44a3-ac7a-36eca9552797} 12 | # NB local variables Real, Integer, Boolean, String 13 | 0 0 0 0 14 | # CONTAINER I/O: [ ] 15 | # Real 16 | 2 2 17 | 0 1 0 2 18 | 1 1 0 0 19 | # Integer 20 | 0 0 21 | # Boolean 22 | 0 0 23 | # String 24 | 0 0 25 | # Inputs of slx.fmu - Real: 26 | 0 27 | # Inputs of slx.fmu - Integer: 28 | 0 29 | # Inputs of slx.fmu - Boolean: 30 | 0 31 | # Inputs of slx.fmu - String: 32 | 0 33 | # Start values of slx.fmu - Real: 34 | 1 35 | 2 0 10.0 36 | # Start values of slx.fmu - Integer: 37 | 0 38 | # Start values of slx.fmu - Boolean: 39 | 0 40 | # Start values of slx.fmu - String: 41 | 0 42 | # Outputs of slx.fmu - Real: 43 | 0 44 | # Outputs of slx.fmu - Integer: 45 | 0 46 | # Outputs of slx.fmu - Boolean: 47 | 0 48 | # Outputs of slx.fmu - String: 49 | 0 50 | -------------------------------------------------------------------------------- /tests/containers/start/REF-modelDescription.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/containers/start/slx.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/containers/start/slx.fmu -------------------------------------------------------------------------------- /tests/containers/start/slx.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "container-slx.fmu", 3 | "fmu": [ "slx.fmu" ], 4 | "auto_parameter":true, 5 | "start": [ 6 | ["slx.fmu", "g_1", 10.0] 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /tests/operations/REF-bouncing_ball-keeponly.csv: -------------------------------------------------------------------------------- 1 | name;newName;valueReference;causality;variability;scalarType;startValue 2 | e;e;5;parameter;tunable;Real;0.7 3 | -------------------------------------------------------------------------------- /tests/operations/REF-bouncing_ball-modified.csv: -------------------------------------------------------------------------------- 1 | h;parameter.h 2 | der(h);out.der(h) 3 | v;out.v 4 | der(v);out.der(v) 5 | g; 6 | e;e 7 | -------------------------------------------------------------------------------- /tests/operations/REF-bouncing_ball-no-tl.csv: -------------------------------------------------------------------------------- 1 | name;newName;valueReference;causality;variability;scalarType;startValue 2 | h;h;0;local;continuous;Real;1 3 | der(h);der(h);1;local;continuous;Real; 4 | v;v;2;local;continuous;Real;0 5 | der(v);der(v);3;local;continuous;Real; 6 | g;g;4;parameter;fixed;Real;9.81 7 | e;e;5;parameter;tunable;Real;0.7 8 | -------------------------------------------------------------------------------- /tests/operations/REF-bouncing_ball-removed.csv: -------------------------------------------------------------------------------- 1 | name;newName;valueReference;causality;variability;scalarType;startValue 2 | h;h;0;local;continuous;Real;1 3 | der(h);der(h);1;local;continuous;Real; 4 | v;v;2;local;continuous;Real;0 5 | der(v);der(v);3;local;continuous;Real; 6 | g;g;4;parameter;fixed;Real;9.81 7 | -------------------------------------------------------------------------------- /tests/operations/REF-bouncing_ball-renamed.csv: -------------------------------------------------------------------------------- 1 | name;newName;valueReference;causality;variability;scalarType;startValue 2 | parameter.h;parameter.h;0;local;continuous;Real;1 3 | out.der(h);out.der(h);1;local;continuous;Real; 4 | out.v;out.v;2;local;continuous;Real;0 5 | out.der(v);out.der(v);3;local;continuous;Real; 6 | e;e;5;parameter;tunable;Real;0.7 7 | -------------------------------------------------------------------------------- /tests/operations/REF-bouncing_ball.csv: -------------------------------------------------------------------------------- 1 | name;newName;valueReference;causality;variability;scalarType;startValue 2 | h;h;0;local;continuous;Real;1 3 | der(h);der(h);1;local;continuous;Real; 4 | v;v;2;local;continuous;Real;0 5 | der(v);der(v);3;local;continuous;Real; 6 | g;g;4;parameter;fixed;Real;9.81 7 | e;e;5;parameter;tunable;Real;0.7 8 | -------------------------------------------------------------------------------- /tests/operations/bouncing_ball-modified.csv: -------------------------------------------------------------------------------- 1 | h;parameter.h 2 | der(h);out.der(h) 3 | v;out.v 4 | der(v);out.der(v) 5 | g; 6 | e;e 7 | -------------------------------------------------------------------------------- /tests/operations/bouncing_ball.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grouperenault/fmu_manipulation_toolbox/d940c0570797aab62d7a6723a3aa6c7bce6eddca/tests/operations/bouncing_ball.fmu -------------------------------------------------------------------------------- /tests/test_suite.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sys 3 | from pathlib import Path 4 | 5 | sys.path.insert(0, str(Path(__file__).parent.parent)) 6 | from fmu_manipulation_toolbox.fmu_operations import * 7 | from fmu_manipulation_toolbox.fmu_container import * 8 | from fmu_manipulation_toolbox.assembly import * 9 | 10 | 11 | class FMUManipulationToolboxTestSuite(unittest.TestCase): 12 | def __init__(self, *args, **kwargs): 13 | super().__init__(*args, **kwargs) 14 | self.fmu_filename = "operations/bouncing_ball.fmu" 15 | 16 | def assert_identical_files(self, filename1, filename2): 17 | with open(filename1, mode="rt", newline=None) as a, open(filename2, mode="rt", newline=None) as b: 18 | self.assertTrue(all(lineA == lineB for lineA, lineB in zip(a, b))) 19 | 20 | def assert_identical_files_but_guid(self, filename1, filename2): 21 | with open(filename1, mode="rt", newline=None) as a, open(filename2, mode="rt", newline=None) as b: 22 | for lineA, lineB in zip(a, b): 23 | if not "guid" in lineA and not lineA == lineB: 24 | return False 25 | return True 26 | 27 | def assert_names_match_ref(self, fmu_filename): 28 | fmu = FMU(fmu_filename) 29 | csv_filename = Path(fmu_filename).with_suffix(".csv") 30 | ref_filename = csv_filename.with_stem("REF-"+csv_filename.stem) 31 | operation = OperationSaveNamesToCSV(csv_filename) 32 | fmu.apply_operation(operation) 33 | self.assert_identical_files(ref_filename, csv_filename) 34 | 35 | def assert_operation_match_ref(self, fmu_filename, operation): 36 | fmu = FMU(self.fmu_filename) 37 | fmu.apply_operation(operation) 38 | fmu.repack(fmu_filename) 39 | self.assert_names_match_ref(fmu_filename) 40 | 41 | def test_strip_top_level(self): 42 | self.assert_operation_match_ref("operations/bouncing_ball-no-tl.fmu", OperationStripTopLevel()) 43 | 44 | def test_save_names_to_CSV(self): 45 | self.assert_names_match_ref("operations/bouncing_ball.fmu") 46 | 47 | def test_rename_from_CSV(self): 48 | self.assert_operation_match_ref("operations/bouncing_ball-renamed.fmu", 49 | OperationRenameFromCSV("operations/bouncing_ball-modified.csv")) 50 | 51 | @unittest.skipUnless(sys.platform.startswith("win"), "Supported only on Windows") 52 | def test_add_remoting_win32(self): 53 | fmu = FMU(self.fmu_filename) 54 | operation = OperationAddRemotingWin32() 55 | fmu.apply_operation(operation) 56 | fmu.repack("bouncing_ball-win32.fmu") 57 | 58 | def test_remove_regexp(self): 59 | self.assert_operation_match_ref("operations/bouncing_ball-removed.fmu", 60 | OperationRemoveRegexp("e")) 61 | 62 | def test_keep_only_regexp(self): 63 | self.assert_operation_match_ref("operations/bouncing_ball-keeponly.fmu", 64 | OperationKeepOnlyRegexp("e")) 65 | 66 | def test_container_bouncing_ball(self): 67 | assembly = Assembly("bouncing.csv", fmu_directory=Path("containers/bouncing_ball"), mt=True, debug=True) 68 | assembly.write_json("bouncing.json") 69 | assembly.make_fmu() 70 | self.assert_identical_files("containers/bouncing_ball/REF-container.txt", 71 | "containers/bouncing_ball/bouncing/resources/container.txt") 72 | self.assert_identical_files("containers/bouncing_ball/REF-bouncing.json", 73 | "containers/bouncing_ball/bouncing.json") 74 | 75 | def test_container_bouncing_ball_profiling(self): 76 | assembly = Assembly("bouncing-profiling.csv", fmu_directory=Path("containers/bouncing_ball"), profiling=True, 77 | debug=True) 78 | assembly.write_json("bouncing-profiling.json") 79 | assembly.make_fmu() 80 | self.assert_identical_files("containers/bouncing_ball/REF-container-profiling.txt", 81 | "containers/bouncing_ball/bouncing-profiling/resources/container.txt") 82 | self.assert_identical_files("containers/bouncing_ball/REF-bouncing-profiling.json", 83 | "containers/bouncing_ball/bouncing-profiling.json") 84 | self.assert_identical_files_but_guid("containers/bouncing_ball/REF-modelDescription-profiling.xml", 85 | "containers/bouncing_ball/bouncing-profiling/modelDescription.xml") 86 | 87 | 88 | def test_container_ssp(self): 89 | assembly = Assembly("bouncing.ssp", fmu_directory=Path("containers/ssp")) 90 | assembly.make_fmu(dump_json=True) 91 | self.assert_identical_files("containers/ssp/REF-bouncing-dump.json", 92 | "containers/ssp/bouncing-dump.json") 93 | 94 | def test_container_json_flat(self): 95 | assembly = Assembly("flat.json", fmu_directory=Path("containers/arch")) 96 | assembly.make_fmu(dump_json=True) 97 | self.assert_identical_files("containers/arch/REF-flat-dump.json", 98 | "containers/arch/flat-dump.json") 99 | 100 | def test_container_subdir_flat(self): 101 | container = FMUContainer("sub.fmu", fmu_directory=Path("containers/arch")) 102 | container.get_fmu("subdir/gain2.fmu") 103 | container.get_fmu("integrate.fmu") 104 | container.get_fmu("sine.fmu") 105 | container.add_implicit_rule() 106 | container.make_fmu("sub.fmu", step_size=0.5) 107 | 108 | def test_container_json_hierarchical(self): 109 | assembly = Assembly("hierarchical.json", fmu_directory=Path("containers/arch")) 110 | assembly.make_fmu(dump_json=True) 111 | self.assert_identical_files("containers/arch/REF-hierarchical-dump.json", 112 | "containers/arch/hierarchical-dump.json") 113 | 114 | def test_container_json_reversed(self): 115 | assembly = Assembly("reversed.json", fmu_directory=Path("containers/arch")) 116 | assembly.make_fmu(dump_json=True) 117 | self.assert_identical_files("containers/arch/REF-reversed-dump.json", 118 | "containers/arch/reversed-dump.json") 119 | 120 | def test_container_start(self): 121 | assembly = Assembly("slx.json", fmu_directory=Path("containers/start"), debug=True) 122 | assembly.make_fmu() 123 | self.assert_identical_files("containers/start/REF-container.txt", 124 | "containers/start/container-slx/resources/container.txt") 125 | self.assert_identical_files_but_guid("containers/start/REF-modelDescription.xml", 126 | "containers/start/container-slx/modelDescription.xml") 127 | 128 | def test_container_move(self): 129 | #bb = Assembly("bouncing.csv", fmu_directory=Path("containers/bouncing_ball")) 130 | #links = bb.root.get_fmu_connections("bb_position.fmu") 131 | #print("Links: ", links) 132 | #bb.write_json("bouncing.json") 133 | assembly = Assembly("nested.json", fmu_directory=Path("containers/arch")) 134 | fmu_name = "fmu1b.fmu" 135 | links_fmu1b = assembly.root.children["level1.fmu"].get_fmu_connections("fmu1b.fmu") 136 | 137 | print("RESULTS:") 138 | for link in links_fmu1b: 139 | print(f"{link}") 140 | 141 | links_fmu0a = assembly.root.get_fmu_connections("fmu0a.fmu") 142 | print("RESULTS:") 143 | for link in links_fmu0a: 144 | print(f"{link}") 145 | 146 | 147 | if __name__ == '__main__': 148 | unittest.main() 149 | --------------------------------------------------------------------------------