├── .clang-format ├── .gitattributes ├── .github └── workflows │ └── ci-conan.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cmake └── project-config.cmake.in ├── conanfile.py ├── include └── cosim.h ├── src ├── cosim.cpp └── lib_info.cpp.in ├── tests ├── connections_test.c ├── data │ ├── fmi1 │ │ └── identity.fmu │ ├── fmi2 │ │ ├── fail.fmu │ │ └── quarter_truck │ │ │ ├── Chassis.fmu │ │ │ ├── LICENSE │ │ │ ├── LogConfig.xml │ │ │ ├── OspSystemStructure.xml │ │ │ ├── README.md │ │ │ ├── Wheel.fmu │ │ │ ├── chassis_OspModelDescription.xml │ │ │ └── wheel_OspModelDescription.xml │ ├── msmi │ │ ├── CraneController_OspModelDescription.xml │ │ └── OspSystemStructure.xml │ └── ssp │ │ └── demo │ │ ├── CraneController.fmu │ │ ├── KnuckleBoomCrane.fmu │ │ ├── KnuckleBoomCrane_OspModelDescription.xml │ │ ├── SystemStructure.ssd │ │ └── no_algorithm_element │ │ └── SystemStructure.ssd ├── ecco_algorithm_multi_bond_test.c ├── execution_from_osp_config_test.c ├── execution_from_ssp_custom_algo_test.c ├── execution_from_ssp_test.c ├── inital_values_test.c ├── load_config_and_teardown_test.c ├── multiple_fmus_execution_test.c ├── observer_can_buffer_samples.c ├── observer_initial_samples_test.c ├── observer_multiple_slaves_test.c ├── simulation_error_handling_test.c ├── single_fmu_execution_test.c ├── time_series_observer_test.c └── variable_metadata_test.c └── version.txt /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | Standard: Cpp11 3 | BasedOnStyle: WebKit 4 | NamespaceIndentation: None 5 | IndentCaseLabels: true 6 | IndentPPDirectives: AfterHash 7 | FixNamespaceComments: true 8 | MaxEmptyLinesToKeep: 2 9 | SpaceAfterTemplateKeyword: false 10 | AllowShortCaseLabelsOnASingleLine: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortIfStatementsOnASingleLine: true 13 | AllowShortBlocksOnASingleLine: true 14 | AllowShortLoopsOnASingleLine: true 15 | BreakBeforeBinaryOperators: None 16 | Cpp11BracedListStyle: true 17 | SpaceBeforeCpp11BracedList: false 18 | BreakBeforeBraces: Custom 19 | BraceWrapping: 20 | AfterEnum: true 21 | AfterStruct: true 22 | AfterClass: true 23 | SplitEmptyFunction: false 24 | AfterFunction: true 25 | AfterNamespace : true 26 | AfterControlStatement: false 27 | IncludeBlocks: Regroup 28 | IncludeCategories: 29 | - Regex: '^[<"]cosim[/.]' 30 | Priority: 20 31 | - Regex: '^[<"](boost|event2|fmilib|gsl|nlohmann|xercesc|zip)[/.]' 32 | Priority: 30 33 | - Regex: '^"' 34 | Priority: 10 35 | - Regex: '.*' 36 | Priority: 40 37 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Declare files that will always have LF line endings on checkout. 5 | conanfile.py text eol=lf 6 | version.txt text eol=lf 7 | -------------------------------------------------------------------------------- /.github/workflows/ci-conan.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # This workflow is triggered on pushes to the repository. 4 | on: [push, workflow_dispatch] 5 | 6 | jobs: 7 | linux: 8 | name: Linux 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | build_type: [Debug, Release] 14 | compiler_version: [9] 15 | option_shared: ['shared=True', 'shared=False'] 16 | option_proxyfmu: ['proxyfmu=True', 'proxyfmu=False'] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Generate Dockerfile 21 | run: | 22 | mkdir /tmp/osp-builder-docker 23 | cat <<'EOF' >/tmp/osp-builder-docker/Dockerfile 24 | FROM conanio/gcc${{ matrix.compiler_version }}-ubuntu16.04 25 | ENV CONAN_LOGIN_USERNAME_OSP=${{ secrets.osp_artifactory_usr }} 26 | ENV CONAN_PASSWORD_OSP=${{ secrets.osp_artifactory_pwd }} 27 | ENV LIBCOSIMC_RUN_TESTS_ON_CONAN_BUILD=1 28 | COPY entrypoint.sh / 29 | ENTRYPOINT /entrypoint.sh 30 | EOF 31 | - name: Generate entrypoint.sh 32 | run: | 33 | cat <<'EOF' >/tmp/osp-builder-docker/entrypoint.sh 34 | #!/bin/bash -v 35 | set -eu 36 | cd /mnt/source 37 | conan remote add osp https://osp.jfrog.io/artifactory/api/conan/conan-local --force 38 | REFNAME="${GITHUB_REF#refs/*/}" 39 | VERSION="v$(") 121 | target_link_libraries(cosimc PUBLIC libcosim::cosim) 122 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 123 | target_link_libraries(cosimc PUBLIC stdc++) 124 | endif() 125 | 126 | if(WIN32 AND NOT BUILD_SHARED_LIBS) 127 | set_target_properties(cosimc PROPERTIES OUTPUT_NAME "libcosimc") 128 | endif() 129 | if(LIBCOSIMC_STANDALONE_INSTALLATION) 130 | set_target_properties(cosimc PROPERTIES INSTALL_RPATH "\$ORIGIN") 131 | endif() 132 | 133 | install( 134 | TARGETS cosimc 135 | EXPORT "${LIBCOSIMC_EXPORT_TARGET}" 136 | ${LIBCOSIMC_INSTALL_DESTINATIONS} 137 | ) 138 | install(DIRECTORY "include/" DESTINATION "${LIBCOSIMC_HEADER_INSTALL_DIR}") 139 | 140 | # ============================================================================== 141 | # API documentation 142 | # ============================================================================== 143 | 144 | find_package(Doxygen) 145 | if(DOXYGEN_FOUND) 146 | message("Found Doxygen, API documentation will be built.") 147 | set(DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output/doc") 148 | set(DOXYGEN_GENERATE_LATEX "NO") 149 | set(DOXYGEN_RECURSIVE "YES") 150 | set(DOXYGEN_SORT_BY_SCOPE_NAME "YES") 151 | set(DOXYGEN_STRIP_FROM_INC_PATH "${CMAKE_SOURCE_DIR}/include") 152 | set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_SOURCE_DIR}/include") 153 | set(DOXYGEN_JAVADOC_AUTOBRIEF "YES") 154 | set(DOXYGEN_QT_AUTOBRIEF "YES") 155 | set(DOXYGEN_MULTILINE_CPP_IS_BRIEF "YES") 156 | set(doxygenInputs "${CMAKE_SOURCE_DIR}/include") 157 | doxygen_add_docs(doc ${doxygenInputs}) 158 | add_custom_target( 159 | install-doc 160 | "${CMAKE_COMMAND}" "-E" "copy_directory" 161 | "${DOXYGEN_OUTPUT_DIRECTORY}" 162 | "${CMAKE_INSTALL_PREFIX}/${LIBCOSIMC_DOC_INSTALL_DIR}/" 163 | DEPENDS doc 164 | ) 165 | else() 166 | message(WARNING "API documentation will not be built since Doxygen was not found.") 167 | endif() 168 | 169 | # ============================================================================== 170 | # Tests 171 | # ============================================================================== 172 | 173 | if(LIBCOSIMC_BUILD_TESTS) 174 | enable_testing() 175 | 176 | set(tests 177 | "connections_test" 178 | "execution_from_osp_config_test" 179 | "execution_from_ssp_custom_algo_test" 180 | "execution_from_ssp_test" 181 | "inital_values_test" 182 | "load_config_and_teardown_test" 183 | "multiple_fmus_execution_test" 184 | "observer_can_buffer_samples" 185 | "observer_initial_samples_test" 186 | "observer_multiple_slaves_test" 187 | "simulation_error_handling_test" 188 | "single_fmu_execution_test" 189 | "time_series_observer_test" 190 | "variable_metadata_test" 191 | "ecco_algorithm_multi_bond_test" 192 | ) 193 | 194 | foreach(testName IN LISTS tests) 195 | add_executable("${testName}" "tests/${testName}.c") 196 | target_link_libraries("${testName}" PRIVATE cosimc) 197 | add_test(NAME "${testName}" COMMAND "${testName}") 198 | set_property( 199 | TEST "${testName}" 200 | PROPERTY ENVIRONMENT "TEST_DATA_DIR=${CMAKE_SOURCE_DIR}/tests/data" 201 | ) 202 | endforeach() 203 | endif() 204 | 205 | # ============================================================================== 206 | # Exports and remaining installation 207 | # ============================================================================== 208 | 209 | install( 210 | FILES "README.md" "LICENSE" 211 | DESTINATION "${LIBCOSIMC_DOC_INSTALL_DIR}" 212 | ) 213 | install( 214 | EXPORT "${LIBCOSIMC_EXPORT_TARGET}" 215 | DESTINATION "${LIBCOSIMC_CMAKE_INSTALL_DIR}" 216 | NAMESPACE "${PROJECT_NAME}::" 217 | ) 218 | 219 | include(CMakePackageConfigHelpers) 220 | 221 | # Generate and install package-config file. 222 | set(configFile "${CMAKE_BINARY_DIR}/${PROJECT_NAME}-config.cmake") 223 | set(targetsFile "${LIBCOSIMC_CMAKE_INSTALL_DIR}/${LIBCOSIMC_EXPORT_TARGET}.cmake") 224 | configure_package_config_file( 225 | "${CMAKE_SOURCE_DIR}/cmake/project-config.cmake.in" 226 | "${configFile}" 227 | INSTALL_DESTINATION "${LIBCOSIMC_CMAKE_INSTALL_DIR}" 228 | PATH_VARS targetsFile 229 | ) 230 | install(FILES "${configFile}" DESTINATION "${LIBCOSIMC_CMAKE_INSTALL_DIR}") 231 | 232 | # Generate and install package-version file 233 | set(versionFile "${CMAKE_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake") 234 | write_basic_package_version_file( 235 | "${versionFile}" 236 | VERSION "${PROJECT_VERSION}" 237 | COMPATIBILITY "SameMajorVersion") 238 | install(FILES "${versionFile}" DESTINATION "${LIBCOSIMC_CMAKE_INSTALL_DIR}") 239 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributor guidelines 2 | ====================== 3 | 4 | This document contains a set of rules and guidelines for everyone who wishes 5 | to contribute to the contents of this repository, hereafter referred to as 6 | "the software". 7 | 8 | 9 | General 10 | ------- 11 | All contributors implicitly agree to license their contribution under the same 12 | terms as the rest of the software. See the [`LICENSE`] file for details. 13 | 14 | All contributions to the software, in the form of changes, removals or 15 | additions to source code and other files under source control, shall be made 16 | via pull requests. A pull request must always be reviewed and merged by someone 17 | other than its author. 18 | 19 | Programming language and style 20 | ------------------------------ 21 | The primary programming language is C++, specifically C++17. 22 | Code should be written in "[modern C++]" style, using high-level types and 23 | constructs when possible. Use the [C++ core guidelines] actively. 24 | 25 | This is especially important when it comes to resources, which should, with 26 | extremely few exceptions, be managed automatically using standard types or 27 | user-defined RAII types. Do not use explicit `new`/`delete` or `malloc`/`free` 28 | other than in low-level code where it is inavoidable. Use [smart pointers] and 29 | [standard containers] rather than raw (owning) pointers. 30 | 31 | Errors are signaled by means of exceptions. The exception to this rule is 32 | functions with a pure C interface, which must necessarily use error codes. 33 | 34 | [`LICENSE`]: ./LICENSE 35 | [modern C++]: https://docs.microsoft.com/en-gb/cpp/cpp/welcome-back-to-cpp-modern-cpp 36 | [C++ core guidelines]: https://github.com/isocpp/CppCoreGuidelines 37 | [smart pointers]: https://en.cppreference.com/w/cpp/header/memory 38 | [standard containers]: https://en.cppreference.com/w/cpp/container 39 | 40 | 41 | Naming and formatting 42 | --------------------- 43 | The following are *strict rules*: 44 | 45 | * **Use [clang-format] to format code**. 46 | Use the `-style=file` option to read the formatting rules from the 47 | `.clang-format` file. 48 | (Rationale: This is a low-effort, low-friction way of enforcing a uniform 49 | code style.) 50 | * **API symbols use the C++ standard library naming conventions.** 51 | This means `snake_case` for namespaces, functions, classes, class members, 52 | and constants, and `SCREAMING_SNAKE_CASE` for macros. 53 | (Rationale: This style is familiar to 100% of C++ programmers, and it 54 | looks and feels consistent when used together with standard 55 | C++ and Boost symbols.) 56 | * **Use a single line feed character to terminate lines, never carriage 57 | return.** 58 | This is configurable in most editors, as well as in Git. 59 | (Rationale: We need to standardise on one thing, and CRLF is just a 60 | pointless Microsoft-specific historic artifact.) 61 | * **Never use the `using` directive (e.g. `using namespace foo;`) in 62 | headers.** 63 | (Rationale: It leads to severe namespace pollution with a high probability 64 | of name clashes.) 65 | * **Only use `using` declarations (e.g. `using foo::bar;`) in headers when the 66 | purpose is to declare symbols which are part of the API.** 67 | (Rationale: Using it simply for convenience leads to pointless namespace 68 | pollution.) 69 | 70 | The following are *recommendations*: 71 | 72 | * Local variables and parameters are in `lowerCamelCase`. 73 | * Template type parameters are in `UpperCamelCase`. 74 | * Private member variables are named differently from local variables, 75 | typically using a trailing underscore (`likeThis_`) or an `m_` prefix 76 | (`m_likeThis`). 77 | * Avoid the `using` directive in source files too. Prefer to use namespace 78 | aliases (e.g. `namespace sn = some_verbosely_named_namespace;`) instead. 79 | 80 | [clang-format]: https://clang.llvm.org 81 | 82 | 83 | Directory structure 84 | ------------------- 85 | `libcosimc` follows the [pitchfork layout](https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs) 86 | and the source directories are organised as follows: 87 | 88 | * `cmake/`: CMake scripts and other build system files. 89 | * `include/`: Header files that define the public API. 90 | * `src/`: Source code. 91 | * `tests/`: Test suite for the library. 92 | * `test/data`: Resources used by the test suite. 93 | 94 | 95 | File names 96 | ---------- 97 | Use the following file extensions: 98 | 99 | * `.h` for pure C headers 100 | * `.c` for pure C sources 101 | * `.cpp` for C++ sources 102 | 103 | Use underscores to separate words in file names (`like_this.hpp`). 104 | 105 | 106 | API documentation 107 | ----------------- 108 | The entire API should be documented, including the non-public API. 109 | Documentation comments in [Doxygen] format should 110 | immediately precede the function/type declarations in the header files. 111 | 112 | For unstable, experimental and/or in-development code, minimal documentation 113 | (i.e., a brief description of what a function does or what a class represents) 114 | is OK. As the API matures and stabilises, higher-quality documentation is 115 | expected. 116 | 117 | For functions, high-quality documentation should include: 118 | 119 | * What the function does. 120 | * Parameter descriptions. 121 | * Return value description. 122 | * Which exceptions may be thrown, and under which circumstances. 123 | * Side effects. 124 | * If the function allocates/acquires resources, where ownership is not made 125 | explicit through the type system (e.g. raw owning pointers): 126 | 127 | - Transfer/acquisition of ownership. 128 | - Requirements or guarantees with regard to lifetime. 129 | 130 | * Preconditions, i.e., any assumptions made by the function about its input, 131 | and which are not guaranteed to be checked. (Typically, these are checked 132 | in debug mode with `assert`.) 133 | 134 | For classes, high-quality documentation should include: 135 | 136 | * For base classes: Requirements and guidelines for subclassing. (Which 137 | methods should/must be implemented, how to use protected members, etc.) 138 | * If the class has reference semantics—i.e., if a copy of an object will 139 | refer to the same data as the original (e.g. `std::shared_ptr`)—this 140 | should be stated. 141 | * Lifetime/ownership issues not expressible through the type system. 142 | 143 | [Doxygen]: http://www.doxygen.org 144 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright the Open Simulation Platform contributors. 2 | 3 | Mozilla Public License Version 2.0 4 | ================================== 5 | 6 | 1. Definitions 7 | -------------- 8 | 9 | 1.1. "Contributor" 10 | means each individual or legal entity that creates, contributes to 11 | the creation of, or owns Covered Software. 12 | 13 | 1.2. "Contributor Version" 14 | means the combination of the Contributions of others (if any) used 15 | by a Contributor and that particular Contributor's Contribution. 16 | 17 | 1.3. "Contribution" 18 | means Covered Software of a particular Contributor. 19 | 20 | 1.4. "Covered Software" 21 | means Source Code Form to which the initial Contributor has attached 22 | the notice in Exhibit A, the Executable Form of such Source Code 23 | Form, and Modifications of such Source Code Form, in each case 24 | including portions thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | (a) that the initial Contributor has attached the notice described 30 | in Exhibit B to the Covered Software; or 31 | 32 | (b) that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the 34 | terms of a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | means any form of the work other than Source Code Form. 38 | 39 | 1.7. "Larger Work" 40 | means a work that combines Covered Software with other material, in 41 | a separate file or files, that is not Covered Software. 42 | 43 | 1.8. "License" 44 | means this document. 45 | 46 | 1.9. "Licensable" 47 | means having the right to grant, to the maximum extent possible, 48 | whether at the time of the initial grant or subsequently, any and 49 | all of the rights conveyed by this License. 50 | 51 | 1.10. "Modifications" 52 | means any of the following: 53 | 54 | (a) any file in Source Code Form that results from an addition to, 55 | deletion from, or modification of the contents of Covered 56 | Software; or 57 | 58 | (b) any new file in Source Code Form that contains any Covered 59 | Software. 60 | 61 | 1.11. "Patent Claims" of a Contributor 62 | means any patent claim(s), including without limitation, method, 63 | process, and apparatus claims, in any patent Licensable by such 64 | Contributor that would be infringed, but for the grant of the 65 | License, by the making, using, selling, offering for sale, having 66 | made, import, or transfer of either its Contributions or its 67 | Contributor Version. 68 | 69 | 1.12. "Secondary License" 70 | means either the GNU General Public License, Version 2.0, the GNU 71 | Lesser General Public License, Version 2.1, the GNU Affero General 72 | Public License, Version 3.0, or any later versions of those 73 | licenses. 74 | 75 | 1.13. "Source Code Form" 76 | means the form of the work preferred for making modifications. 77 | 78 | 1.14. "You" (or "Your") 79 | means an individual or a legal entity exercising rights under this 80 | License. For legal entities, "You" includes any entity that 81 | controls, is controlled by, or is under common control with You. For 82 | purposes of this definition, "control" means (a) the power, direct 83 | or indirect, to cause the direction or management of such entity, 84 | whether by contract or otherwise, or (b) ownership of more than 85 | fifty percent (50%) of the outstanding shares or beneficial 86 | ownership of such entity. 87 | 88 | 2. License Grants and Conditions 89 | -------------------------------- 90 | 91 | 2.1. Grants 92 | 93 | Each Contributor hereby grants You a world-wide, royalty-free, 94 | non-exclusive license: 95 | 96 | (a) under intellectual property rights (other than patent or trademark) 97 | Licensable by such Contributor to use, reproduce, make available, 98 | modify, display, perform, distribute, and otherwise exploit its 99 | Contributions, either on an unmodified basis, with Modifications, or 100 | as part of a Larger Work; and 101 | 102 | (b) under Patent Claims of such Contributor to make, use, sell, offer 103 | for sale, have made, import, and otherwise transfer either its 104 | Contributions or its Contributor Version. 105 | 106 | 2.2. Effective Date 107 | 108 | The licenses granted in Section 2.1 with respect to any Contribution 109 | become effective for each Contribution on the date the Contributor first 110 | distributes such Contribution. 111 | 112 | 2.3. Limitations on Grant Scope 113 | 114 | The licenses granted in this Section 2 are the only rights granted under 115 | this License. No additional rights or licenses will be implied from the 116 | distribution or licensing of Covered Software under this License. 117 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 118 | Contributor: 119 | 120 | (a) for any code that a Contributor has removed from Covered Software; 121 | or 122 | 123 | (b) for infringements caused by: (i) Your and any other third party's 124 | modifications of Covered Software, or (ii) the combination of its 125 | Contributions with other software (except as part of its Contributor 126 | Version); or 127 | 128 | (c) under Patent Claims infringed by Covered Software in the absence of 129 | its Contributions. 130 | 131 | This License does not grant any rights in the trademarks, service marks, 132 | or logos of any Contributor (except as may be necessary to comply with 133 | the notice requirements in Section 3.4). 134 | 135 | 2.4. Subsequent Licenses 136 | 137 | No Contributor makes additional grants as a result of Your choice to 138 | distribute the Covered Software under a subsequent version of this 139 | License (see Section 10.2) or under the terms of a Secondary License (if 140 | permitted under the terms of Section 3.3). 141 | 142 | 2.5. Representation 143 | 144 | Each Contributor represents that the Contributor believes its 145 | Contributions are its original creation(s) or it has sufficient rights 146 | to grant the rights to its Contributions conveyed by this License. 147 | 148 | 2.6. Fair Use 149 | 150 | This License is not intended to limit any rights You have under 151 | applicable copyright doctrines of fair use, fair dealing, or other 152 | equivalents. 153 | 154 | 2.7. Conditions 155 | 156 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 157 | in Section 2.1. 158 | 159 | 3. Responsibilities 160 | ------------------- 161 | 162 | 3.1. Distribution of Source Form 163 | 164 | All distribution of Covered Software in Source Code Form, including any 165 | Modifications that You create or to which You contribute, must be under 166 | the terms of this License. You must inform recipients that the Source 167 | Code Form of the Covered Software is governed by the terms of this 168 | License, and how they can obtain a copy of this License. You may not 169 | attempt to alter or restrict the recipients' rights in the Source Code 170 | Form. 171 | 172 | 3.2. Distribution of Executable Form 173 | 174 | If You distribute Covered Software in Executable Form then: 175 | 176 | (a) such Covered Software must also be made available in Source Code 177 | Form, as described in Section 3.1, and You must inform recipients of 178 | the Executable Form how they can obtain a copy of such Source Code 179 | Form by reasonable means in a timely manner, at a charge no more 180 | than the cost of distribution to the recipient; and 181 | 182 | (b) You may distribute such Executable Form under the terms of this 183 | License, or sublicense it under different terms, provided that the 184 | license for the Executable Form does not attempt to limit or alter 185 | the recipients' rights in the Source Code Form under this License. 186 | 187 | 3.3. Distribution of a Larger Work 188 | 189 | You may create and distribute a Larger Work under terms of Your choice, 190 | provided that You also comply with the requirements of this License for 191 | the Covered Software. If the Larger Work is a combination of Covered 192 | Software with a work governed by one or more Secondary Licenses, and the 193 | Covered Software is not Incompatible With Secondary Licenses, this 194 | License permits You to additionally distribute such Covered Software 195 | under the terms of such Secondary License(s), so that the recipient of 196 | the Larger Work may, at their option, further distribute the Covered 197 | Software under the terms of either this License or such Secondary 198 | License(s). 199 | 200 | 3.4. Notices 201 | 202 | You may not remove or alter the substance of any license notices 203 | (including copyright notices, patent notices, disclaimers of warranty, 204 | or limitations of liability) contained within the Source Code Form of 205 | the Covered Software, except that You may alter any license notices to 206 | the extent required to remedy known factual inaccuracies. 207 | 208 | 3.5. Application of Additional Terms 209 | 210 | You may choose to offer, and to charge a fee for, warranty, support, 211 | indemnity or liability obligations to one or more recipients of Covered 212 | Software. However, You may do so only on Your own behalf, and not on 213 | behalf of any Contributor. You must make it absolutely clear that any 214 | such warranty, support, indemnity, or liability obligation is offered by 215 | You alone, and You hereby agree to indemnify every Contributor for any 216 | liability incurred by such Contributor as a result of warranty, support, 217 | indemnity or liability terms You offer. You may include additional 218 | disclaimers of warranty and limitations of liability specific to any 219 | jurisdiction. 220 | 221 | 4. Inability to Comply Due to Statute or Regulation 222 | --------------------------------------------------- 223 | 224 | If it is impossible for You to comply with any of the terms of this 225 | License with respect to some or all of the Covered Software due to 226 | statute, judicial order, or regulation then You must: (a) comply with 227 | the terms of this License to the maximum extent possible; and (b) 228 | describe the limitations and the code they affect. Such description must 229 | be placed in a text file included with all distributions of the Covered 230 | Software under this License. Except to the extent prohibited by statute 231 | or regulation, such description must be sufficiently detailed for a 232 | recipient of ordinary skill to be able to understand it. 233 | 234 | 5. Termination 235 | -------------- 236 | 237 | 5.1. The rights granted under this License will terminate automatically 238 | if You fail to comply with any of its terms. However, if You become 239 | compliant, then the rights granted under this License from a particular 240 | Contributor are reinstated (a) provisionally, unless and until such 241 | Contributor explicitly and finally terminates Your grants, and (b) on an 242 | ongoing basis, if such Contributor fails to notify You of the 243 | non-compliance by some reasonable means prior to 60 days after You have 244 | come back into compliance. Moreover, Your grants from a particular 245 | Contributor are reinstated on an ongoing basis if such Contributor 246 | notifies You of the non-compliance by some reasonable means, this is the 247 | first time You have received notice of non-compliance with this License 248 | from such Contributor, and You become compliant prior to 30 days after 249 | Your receipt of the notice. 250 | 251 | 5.2. If You initiate litigation against any entity by asserting a patent 252 | infringement claim (excluding declaratory judgment actions, 253 | counter-claims, and cross-claims) alleging that a Contributor Version 254 | directly or indirectly infringes any patent, then the rights granted to 255 | You by any and all Contributors for the Covered Software under Section 256 | 2.1 of this License shall terminate. 257 | 258 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 259 | end user license agreements (excluding distributors and resellers) which 260 | have been validly granted by You or Your distributors under this License 261 | prior to termination shall survive termination. 262 | 263 | ************************************************************************ 264 | * * 265 | * 6. Disclaimer of Warranty * 266 | * ------------------------- * 267 | * * 268 | * Covered Software is provided under this License on an "as is" * 269 | * basis, without warranty of any kind, either expressed, implied, or * 270 | * statutory, including, without limitation, warranties that the * 271 | * Covered Software is free of defects, merchantable, fit for a * 272 | * particular purpose or non-infringing. The entire risk as to the * 273 | * quality and performance of the Covered Software is with You. * 274 | * Should any Covered Software prove defective in any respect, You * 275 | * (not any Contributor) assume the cost of any necessary servicing, * 276 | * repair, or correction. This disclaimer of warranty constitutes an * 277 | * essential part of this License. No use of any Covered Software is * 278 | * authorized under this License except under this disclaimer. * 279 | * * 280 | ************************************************************************ 281 | 282 | ************************************************************************ 283 | * * 284 | * 7. Limitation of Liability * 285 | * -------------------------- * 286 | * * 287 | * Under no circumstances and under no legal theory, whether tort * 288 | * (including negligence), contract, or otherwise, shall any * 289 | * Contributor, or anyone who distributes Covered Software as * 290 | * permitted above, be liable to You for any direct, indirect, * 291 | * special, incidental, or consequential damages of any character * 292 | * including, without limitation, damages for lost profits, loss of * 293 | * goodwill, work stoppage, computer failure or malfunction, or any * 294 | * and all other commercial damages or losses, even if such party * 295 | * shall have been informed of the possibility of such damages. This * 296 | * limitation of liability shall not apply to liability for death or * 297 | * personal injury resulting from such party's negligence to the * 298 | * extent applicable law prohibits such limitation. Some * 299 | * jurisdictions do not allow the exclusion or limitation of * 300 | * incidental or consequential damages, so this exclusion and * 301 | * limitation may not apply to You. * 302 | * * 303 | ************************************************************************ 304 | 305 | 8. Litigation 306 | ------------- 307 | 308 | Any litigation relating to this License may be brought only in the 309 | courts of a jurisdiction where the defendant maintains its principal 310 | place of business and such litigation shall be governed by laws of that 311 | jurisdiction, without reference to its conflict-of-law provisions. 312 | Nothing in this Section shall prevent a party's ability to bring 313 | cross-claims or counter-claims. 314 | 315 | 9. Miscellaneous 316 | ---------------- 317 | 318 | This License represents the complete agreement concerning the subject 319 | matter hereof. If any provision of this License is held to be 320 | unenforceable, such provision shall be reformed only to the extent 321 | necessary to make it enforceable. Any law or regulation which provides 322 | that the language of a contract shall be construed against the drafter 323 | shall not be used to construe this License against a Contributor. 324 | 325 | 10. Versions of the License 326 | --------------------------- 327 | 328 | 10.1. New Versions 329 | 330 | Mozilla Foundation is the license steward. Except as provided in Section 331 | 10.3, no one other than the license steward has the right to modify or 332 | publish new versions of this License. Each version will be given a 333 | distinguishing version number. 334 | 335 | 10.2. Effect of New Versions 336 | 337 | You may distribute the Covered Software under the terms of the version 338 | of the License under which You originally received the Covered Software, 339 | or under the terms of any subsequent version published by the license 340 | steward. 341 | 342 | 10.3. Modified Versions 343 | 344 | If you create software not governed by this License, and you want to 345 | create a new license for such software, you may create and use a 346 | modified version of this License if you rename the license and remove 347 | any references to the name of the license steward (except to note that 348 | such modified license differs from this License). 349 | 350 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 351 | Licenses 352 | 353 | If You choose to distribute Source Code Form that is Incompatible With 354 | Secondary Licenses under the terms of this version of the License, the 355 | notice described in Exhibit B of this License must be attached. 356 | 357 | Exhibit A - Source Code Form License Notice 358 | ------------------------------------------- 359 | 360 | This Source Code Form is subject to the terms of the Mozilla Public 361 | License, v. 2.0. If a copy of the MPL was not distributed with this 362 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 363 | 364 | If it is not possible or desirable to put the notice in a particular 365 | file, then You may include the notice in a location (such as a LICENSE 366 | file in a relevant directory) where a recipient would be likely to look 367 | for such a notice. 368 | 369 | You may add additional accurate notices of copyright ownership. 370 | 371 | Exhibit B - "Incompatible With Secondary Licenses" Notice 372 | --------------------------------------------------------- 373 | 374 | This Source Code Form is "Incompatible With Secondary Licenses", as 375 | defined by the Mozilla Public License, v. 2.0. 376 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libcosimc - OSP C co-simulation API 2 | =================================== 3 | ![libcosimc CI Conan](https://github.com/open-simulation-platform/libcosimc/workflows/libcosimc%20CI%20Conan/badge.svg) 4 | 5 | This repository contains the [OSP] C library for co-simulations, which wraps and 6 | exposes a subset of the [libcosim] C++ library's functions. 7 | 8 | See [`CONTRIBUTING.md`] for contributor guidelines and [`LICENSE`] for 9 | terms of use. 10 | 11 | How to build 12 | ------------ 13 | Please read the [libcosim build instructions]. The commands you should run 14 | to build libcosimc are exactly the same, except that the option you need to 15 | add to `conan install` to enable [proxy-fmu] support is 16 | `--options="libcosim/*:proxyfmu=True`. 17 | 18 | [`CONTRIBUTING.md`]: ./CONTRIBUTING.md 19 | [libcosim]: https://github.com/open-simulation-platform/libcosim 20 | [libcosim build instructions]: https://github.com/open-simulation-platform/libcosim#how-to-build 21 | [`LICENSE`]: ./LICENSE 22 | [OSP]: https://opensimulationplatform.com/ 23 | [proxy-fmu]: https://github.com/open-simulation-platform/proxy-fmu/ 24 | -------------------------------------------------------------------------------- /cmake/project-config.cmake.in: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | find_dependency(libcosim REQUIRED) 3 | @PACKAGE_INIT@ 4 | include ("@PACKAGE_targetsFile@") 5 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from conan import ConanFile 4 | from conan.tools.cmake import CMake, cmake_layout 5 | from conan.tools.env import VirtualRunEnv 6 | from conan.tools.files import load, copy 7 | 8 | 9 | class LibCosimCConan(ConanFile): 10 | # Basic package info 11 | name = "libcosimc" 12 | 13 | def set_version(self): 14 | self.version = load(self, os.path.join(self.recipe_folder, "version.txt")).strip() 15 | 16 | # Metadata 17 | license = "MPL-2.0" 18 | author = "osp" 19 | description = "A C wrapper for libcosim, a co-simulation library for C++" 20 | 21 | # Binary configuration 22 | package_type = "library" 23 | settings = "os", "compiler", "build_type", "arch" 24 | options = { 25 | "shared": [True, False], 26 | "fPIC": [True, False], 27 | } 28 | default_options = { 29 | "shared": True, 30 | "fPIC": True, 31 | } 32 | 33 | def config_options(self): 34 | if self.settings.os == "Windows": 35 | del self.options.fPIC 36 | 37 | def configure(self): 38 | if self.options.shared: 39 | self.options.rm_safe("fPIC") 40 | self.options["*"].shared = self.options.shared 41 | 42 | # Dependencies/requirements 43 | tool_requires = ( 44 | "cmake/[>=3.15]", 45 | "doxygen/1.9.1", 46 | ) 47 | requires = ( 48 | "libcosim/0.11.0@osp/stable", 49 | ) 50 | 51 | # Exports 52 | exports = "version.txt" 53 | exports_sources = "*" 54 | 55 | # Build steps 56 | generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv" 57 | 58 | def layout(self): 59 | cmake_layout(self) 60 | 61 | def build(self): 62 | cmake = CMake(self) 63 | cmake.configure() 64 | cmake.build() 65 | cmake.build(target="doc") 66 | if self._is_tests_enabled(): 67 | env = VirtualRunEnv(self).environment() 68 | env.define("CTEST_OUTPUT_ON_FAILURE", "ON") 69 | with env.vars(self).apply(): 70 | cmake.test() 71 | 72 | # Packaging 73 | def package(self): 74 | cmake = CMake(self) 75 | cmake.install() 76 | cmake.build(target="install-doc") 77 | 78 | def package_info(self): 79 | self.cpp_info.libs = [ "cosimc" ] 80 | # Ensure that consumers use our CMake package configuration files 81 | # rather than ones generated by Conan. 82 | self.cpp_info.set_property("cmake_find_mode", "none") 83 | self.cpp_info.builddirs.append(".") 84 | 85 | # Helper functions 86 | def _is_tests_enabled(self): 87 | return os.getenv("LIBCOSIMC_RUN_TESTS_ON_CONAN_BUILD", "False").lower() in ("true", "1") 88 | -------------------------------------------------------------------------------- /include/cosim.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * Main header file 4 | * \copyright 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 8 | */ 9 | #ifndef COSIM_H 10 | #define COSIM_H 11 | 12 | #ifndef __cplusplus 13 | # include 14 | #endif 15 | #include 16 | #include 17 | 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | 24 | /// The type used to specify (simulation) time points. The time unit is nanoseconds. 25 | typedef int64_t cosim_time_point; 26 | 27 | /// The type used to specify (simulation) time durations. The time unit is nanoseconds. 28 | typedef int64_t cosim_duration; 29 | 30 | /// value reference. 31 | typedef uint32_t cosim_value_reference; 32 | 33 | /// Slave index. 34 | typedef int cosim_slave_index; 35 | 36 | /// Step number 37 | typedef int64_t cosim_step_number; 38 | 39 | /// Error codes. 40 | typedef enum 41 | { 42 | COSIM_ERRC_SUCCESS = 0, 43 | 44 | // --- Codes unique to the C API --- 45 | 46 | /// Unspecified error (but message may contain details). 47 | COSIM_ERRC_UNSPECIFIED, 48 | 49 | /// Error reported by C/C++ runtime; check `errno` to get the right code. 50 | COSIM_ERRC_ERRNO, 51 | 52 | /// Invalid function argument. 53 | COSIM_ERRC_INVALID_ARGUMENT, 54 | 55 | /// Function may not be called while in this state. 56 | COSIM_ERRC_ILLEGAL_STATE, 57 | 58 | /// Index out of range. 59 | COSIM_ERRC_OUT_OF_RANGE, 60 | 61 | /** 62 | * The time step failed, but can be retried with a shorter step length 63 | * (if supported by all slaves). 64 | */ 65 | COSIM_ERRC_STEP_TOO_LONG, 66 | 67 | // --- Codes that correspond to C++ API error conditions --- 68 | 69 | /// An input file is corrupted or invalid. 70 | COSIM_ERRC_BAD_FILE, 71 | 72 | /// The requested feature (e.g. an FMI feature) is unsupported. 73 | COSIM_ERRC_UNSUPPORTED_FEATURE, 74 | 75 | /// Error loading dynamic library (e.g. model code). 76 | COSIM_ERRC_DL_LOAD_ERROR, 77 | 78 | /// The model reported an error. 79 | COSIM_ERRC_MODEL_ERROR, 80 | 81 | /// An error occured during simulation. 82 | COSIM_ERRC_SIMULATION_ERROR, 83 | 84 | /// ZIP file error. 85 | COSIM_ERRC_ZIP_ERROR, 86 | } cosim_errc; 87 | 88 | 89 | /** 90 | * Returns the error code associated with the last reported error. 91 | * 92 | * Most functions in this library will indicate that an error occurred by 93 | * returning -1 or `NULL`, after which this function can be called to 94 | * obtain more detailed information about the problem. 95 | * 96 | * This function must be called from the thread in which the error occurred, 97 | * and before any new calls to functions in this library (with the exception 98 | * of `cosim_last_error_message()`). 99 | * 100 | * \returns 101 | * The error code associated with the last reported error. 102 | */ 103 | cosim_errc cosim_last_error_code(); 104 | 105 | 106 | /** 107 | * Returns a textual description of the last reported error. 108 | * 109 | * Most functions in this library will indicate that an error occurred by 110 | * returning -1 or `NULL`, after which this function can be called to 111 | * obtain more detailed information about the problem. 112 | * 113 | * This function must be called from the thread in which the error occurred, 114 | * and before any new calls to functions in this library (with the exception 115 | * of `cosim_last_error_code()`). 116 | * 117 | * \returns 118 | * A textual description of the last reported error. The pointer is 119 | * only guaranteed to remain valid until the next time a function in 120 | * this library is called (with the exception of `cosim_last_error_code()`). 121 | */ 122 | const char* cosim_last_error_message(); 123 | 124 | 125 | struct cosim_execution_s; 126 | 127 | /// An opaque object which contains the state for an execution. 128 | typedef struct cosim_execution_s cosim_execution; 129 | 130 | 131 | struct cosim_algorithm_s; 132 | 133 | /// An opaque object which contains the configuration for a cosimulation algorithm. 134 | typedef struct cosim_algorithm_s cosim_algorithm; 135 | 136 | /** 137 | * Creates a new execution. 138 | * 139 | * \param [in] startTime 140 | * The (logical) time point at which the simulation should start. 141 | * \param [in] stepSize 142 | * The execution step size. 143 | * \returns 144 | * A pointer to an object which holds the execution state, 145 | * or NULL on error. 146 | */ 147 | cosim_execution* cosim_execution_create( 148 | cosim_time_point startTime, 149 | cosim_duration stepSize); 150 | 151 | /** 152 | * Creates an ecco algorithm with the specified parameters. 153 | * \param [in] safetyFactor Safety factor 154 | * \param [in] stepSize Initial step size 155 | * \param [in] minStepSize Minimum step size 156 | * \param [in] maxStepSize Maximum step size 157 | * \param [in] minChangeRate Minimum rate of change in step size 158 | * \param [in] maxChangeRate Maximum rate of change in step size 159 | * \param [in] absTolerance Absolute tolerance for deciding mismatch in the residual power 160 | * \param [in] relTolerance Relative tolerance for deciding mismatch in the residual power 161 | * \param [in] pGain Proportional value in the PI controller 162 | * \param [in] iGain Integral value in the PI controller 163 | * \returns A pointer to a new instance of cosim_algorithm, or NULL if an error occurred. 164 | */ 165 | cosim_algorithm* cosim_ecco_algorithm_create( 166 | double safetyFactor, 167 | double stepSize, 168 | double minStepSize, 169 | double maxStepSize, 170 | double minChangeRate, 171 | double maxChangeRate, 172 | double absTolerance, 173 | double relTolerance, 174 | double pGain, 175 | double iGain); 176 | 177 | /** 178 | * Creates a power bond between two instances of models 179 | * \param [in] Ecco An algorithm instance 180 | * \param [in] index1 Slave index for the first model 181 | * \param [in] v1 The output of the first model 182 | * \param [in] u1 The input of the first model 183 | * \param [in] index2 Slave index Id for the second model 184 | * \param [in] v2 The output of the second model 185 | * \param [in] u2 The input of the second model 186 | * \returns 187 | * 0 on success and -1 on error. 188 | */ 189 | int cosim_ecco_add_power_bond( 190 | cosim_algorithm* algo, 191 | cosim_slave_index m1Index, 192 | cosim_value_reference v1, 193 | cosim_value_reference u1, 194 | cosim_slave_index m2Index, 195 | cosim_value_reference v2, 196 | cosim_value_reference u2); 197 | 198 | /** 199 | * Creates a fixed step algorithm 200 | * \param [in] stepSize 201 | * The execution step size. 202 | * \returns A pointer to a new instance of cosim_algorithm, or NULL if an error occurred. 203 | */ 204 | cosim_algorithm* cosim_fixed_step_algorithm_create(cosim_duration stepSize); 205 | 206 | /** 207 | * Destroys a co-simulation algorithm . 208 | * 209 | * \returns 210 | * 0 on success and -1 on error. 211 | */ 212 | int cosim_algorithm_destroy(cosim_algorithm* algorithm); 213 | 214 | /** 215 | * Creates a new execution with the given co-simulation algorithm. 216 | * 217 | * \param [in] startTime 218 | * The (logical) time point at which the simulation should start. 219 | * \param [in] algo* 220 | * Co-simulation algorithm object 221 | * \returns 222 | * A pointer to an object which holds the execution state, 223 | * or NULL on error. 224 | */ 225 | cosim_execution* cosim_execution_create_with_algorithm(cosim_time_point startTime, cosim_algorithm* algo); 226 | 227 | /** 228 | * Creates a new execution based on an OspSystemStructure.xml file. 229 | * 230 | * \param [in] configPath 231 | * Path to an OspSystemStructure.xml file, or a directory holding OspSystemStructure.xml 232 | * \param [in] startTimeDefined 233 | * Defines whether or not the following startTime variable should be ignored or not. 234 | * \param [in] startTime 235 | * The (logical) time point at which the simulation should start. 236 | * If startTimeDefined=false, this variable will be ignored and a default value will be used. 237 | * \returns 238 | * A pointer to an object which holds the execution state, 239 | * or NULL on error. 240 | */ 241 | cosim_execution* cosim_osp_config_execution_create( 242 | const char* configPath, 243 | bool startTimeDefined, 244 | cosim_time_point startTime); 245 | 246 | /** 247 | * Creates a new execution based on a SystemStructure.ssd file. 248 | * 249 | * \param [in] sspDir 250 | * Path to an .ssd file, or a directory holding SystemStructure.ssd 251 | * \param [in] startTimeDefined 252 | * Defines whether or not the following startTime variable should be ignored or not. 253 | * \param [in] startTime 254 | * The (logical) time point at which the simulation should start. 255 | * If startTimeDefined=false, this variable will be ignored and a default value will be used. 256 | * \returns 257 | * A pointer to an object which holds the execution state, 258 | * or NULL on error. 259 | */ 260 | cosim_execution* cosim_ssp_execution_create( 261 | const char* sspDir, 262 | bool startTimeDefined, 263 | cosim_time_point startTime); 264 | 265 | /** 266 | * Creates a new execution based on a SystemStructure.ssd file. 267 | * 268 | * \param [in] sspDir 269 | * Path to the directory holding SystemStructure.ssd 270 | * \param [in] startTimeDefined 271 | * Defines whether or not the following startTime variable should be ignored or not. 272 | * \param [in] startTime 273 | * The (logical) time point at which the simulation should start. 274 | * If startTimeDefined=false, this variable will be ignored and a default value will be used. 275 | * \param [in] stepSize 276 | * The stepSize that will be used by the (fixed-step) co-simulation algorithm. 277 | * \returns 278 | * A pointer to an object which holds the execution state, 279 | * or NULL on error. 280 | */ 281 | cosim_execution* cosim_ssp_fixed_step_execution_create( 282 | const char* sspDir, 283 | bool startTimeDefined, 284 | cosim_time_point startTime, 285 | cosim_duration stepSize); 286 | 287 | /** 288 | * Destroys an execution. 289 | * 290 | * \returns 291 | * 0 on success and -1 on error. 292 | */ 293 | int cosim_execution_destroy(cosim_execution* execution); 294 | 295 | struct cosim_slave_s; 296 | 297 | /// An opaque object which contains the state for a slave. 298 | typedef struct cosim_slave_s cosim_slave; 299 | 300 | 301 | /** 302 | * Creates a new local slave. 303 | * 304 | * \param [in] fmuPath 305 | * Path to FMU. 306 | * \param [in] instanceName 307 | * Unique name of the instance. 308 | * 309 | * \returns 310 | * A pointer to an object which holds the local slave object, 311 | * or NULL on error. 312 | */ 313 | cosim_slave* cosim_local_slave_create(const char* fmuPath, const char* instanceName); 314 | 315 | /** 316 | * Sets a real initial value for the given slave in the given execution. 317 | * 318 | * \param [in] execution 319 | * The execution. 320 | * \param[in] slaveIndex 321 | * The slave. 322 | * \param vr 323 | * The value_reference. 324 | * \param value 325 | * The initial value. 326 | * \returns 327 | * 0 on success and -1 on error. 328 | */ 329 | int cosim_execution_set_real_initial_value(cosim_execution* execution, cosim_slave_index slaveIndex, cosim_value_reference vr, double value); 330 | 331 | /** 332 | * Sets a integer initial value for the given slave in the given execution. 333 | * 334 | * \param [in] execution 335 | * The execution. 336 | * \param[in] slaveIndex 337 | * The slave. 338 | * \param vr 339 | * The value_reference. 340 | * \param value 341 | * The initial value. 342 | * \returns 343 | * 0 on success and -1 on error. 344 | */ 345 | int cosim_execution_set_integer_initial_value(cosim_execution* execution, cosim_slave_index slaveIndex, cosim_value_reference vr, int value); 346 | 347 | /** 348 | * Sets a boolean initial value for the given slave in the given execution. 349 | * 350 | * \param [in] execution 351 | * The execution. 352 | * \param[in] slaveIndex 353 | * The slave. 354 | * \param vr 355 | * The value_reference. 356 | * \param value 357 | * The initial value. 358 | * \returns 359 | * 0 on success and -1 on error. 360 | */ 361 | int cosim_execution_set_boolean_initial_value(cosim_execution* execution, cosim_slave_index slaveIndex, cosim_value_reference vr, bool value); 362 | 363 | /** 364 | * Sets a string initial value for the given slave in the given execution. 365 | * 366 | * \param [in] execution 367 | * The execution. 368 | * \param[in] slaveIndex 369 | * The slave. 370 | * \param vr 371 | * The value_reference. 372 | * \param value 373 | * The initial value. 374 | * \returns 375 | * 0 on success and -1 on error. 376 | */ 377 | int cosim_execution_set_string_initial_value(cosim_execution* execution, cosim_slave_index slaveIndex, cosim_value_reference vr, char* value); 378 | 379 | 380 | /** 381 | * Destroys a local slave. 382 | * 383 | * \returns 384 | * 0 on success and -1 on error. 385 | */ 386 | int cosim_local_slave_destroy(cosim_slave* slave); 387 | 388 | 389 | /** 390 | * Loads a co-simulation FMU, instantiates a slave based on it, and adds it 391 | * to an execution. 392 | * 393 | * The slave is owned by the execution and is destroyed along with it. 394 | * 395 | * \param [in] execution 396 | * The execution to which the slave should be added. 397 | * \param [in] slave 398 | * A pointer to a slave, which may not be null. The slave may not previously have been added to any execution. 399 | * 400 | * \returns 401 | * The slave's unique index in the execution, or -1 on error. 402 | */ 403 | cosim_slave_index cosim_execution_add_slave( 404 | cosim_execution* execution, 405 | cosim_slave* slave); 406 | 407 | 408 | /** 409 | * Advances an execution a number of time steps. 410 | * 411 | * \param [in] execution 412 | * The execution to be stepped. 413 | * \param [in] numSteps 414 | * The number of steps to advance the simulation execution. 415 | * 416 | * \returns 417 | * 0 on success and -1 on error. 418 | */ 419 | int cosim_execution_step(cosim_execution* execution, size_t numSteps); 420 | 421 | /** 422 | * Advances an execution to a specific point in time (blocking). 423 | * 424 | * \param [in] execution 425 | * The execution to be stepped. 426 | * \param [in] targetTime 427 | * The point in time, which to advance the simulation execution. 428 | * 429 | * \returns 430 | * -1 on error, 0 if the simulation was stopped prior to reaching the specified targetTime 431 | * and 1 if the simulation was successfully advanced to the specified targetTime. 432 | */ 433 | int cosim_execution_simulate_until(cosim_execution* execution, cosim_time_point targetTime); 434 | 435 | 436 | /** 437 | * Starts an execution (non blocking). 438 | * 439 | * The execution will run until `cosim_execution_stop()` is called. The status of the 440 | * simulation can be polled with `cosim_execution_get_status()`. 441 | * 442 | * \param [in] execution 443 | * The execution to be started. 444 | * 445 | * \returns 446 | * 0 on success and -1 on error. 447 | */ 448 | int cosim_execution_start(cosim_execution* execution); 449 | 450 | /** 451 | * Stops an execution. 452 | * 453 | * \param [in] execution 454 | * The execution to be stopped. 455 | * 456 | * \returns 457 | * 0 on success and -1 on error. 458 | */ 459 | int cosim_execution_stop(cosim_execution* execution); 460 | 461 | 462 | /// Enables real time simulation for an execution. 463 | int cosim_execution_enable_real_time_simulation(cosim_execution* execution); 464 | 465 | /// Disables real time simulation for an execution. 466 | int cosim_execution_disable_real_time_simulation(cosim_execution* execution); 467 | 468 | /// Sets a custom real time factor. 469 | int cosim_execution_set_real_time_factor_target(cosim_execution* execution, double realTimeFactor); 470 | 471 | /// Sets the number of steps to monitor for rolling average real time factor measurement. 472 | int cosim_execution_set_steps_to_monitor(cosim_execution* execution, int stepsToMonitor); 473 | 474 | 475 | /// Execution states. 476 | typedef enum 477 | { 478 | COSIM_EXECUTION_STOPPED, 479 | COSIM_EXECUTION_RUNNING, 480 | COSIM_EXECUTION_ERROR 481 | } cosim_execution_state; 482 | 483 | /// A struct containing the execution status. 484 | typedef struct 485 | { 486 | /// Current simulation time. 487 | cosim_time_point current_time; 488 | /// Current execution state. 489 | cosim_execution_state state; 490 | /// Last recorded error code. 491 | int error_code; 492 | /// Total average real time factor. 493 | double total_average_real_time_factor; 494 | /// Rolling average real time factor. 495 | double rolling_average_real_time_factor; 496 | /// Current real time factor target. 497 | double real_time_factor_target; 498 | /// Executing towards real time target. 499 | int is_real_time_simulation; 500 | /// Number of steps used in rolling average real time factor measurement. 501 | int steps_to_monitor; 502 | } cosim_execution_status; 503 | 504 | /** 505 | * Returns execution status. 506 | * 507 | * This method will also poll the status of any asynchronous execution started 508 | * by calling `cosim_execution_start()`. Will return failure if a simulation 509 | * error occured during the execution, in which case the `status` parameter 510 | * will still be valid. 511 | * 512 | * \param [in] execution 513 | * The execution to get status from. 514 | * \param [out] status 515 | * A pointer to a cosim_execution_status that will be filled with actual 516 | * execution status. 517 | * 518 | * \returns 519 | * 0 on success and -1 on error. 520 | */ 521 | int cosim_execution_get_status( 522 | cosim_execution* execution, 523 | cosim_execution_status* status); 524 | 525 | /// Max number of characters used for slave name and source. 526 | #define SLAVE_NAME_MAX_SIZE 1024 527 | 528 | /// Variable types. 529 | typedef enum 530 | { 531 | COSIM_VARIABLE_TYPE_REAL, 532 | COSIM_VARIABLE_TYPE_INTEGER, 533 | COSIM_VARIABLE_TYPE_STRING, 534 | COSIM_VARIABLE_TYPE_BOOLEAN, 535 | } cosim_variable_type; 536 | 537 | /// Variable causalities. 538 | typedef enum 539 | { 540 | COSIM_VARIABLE_CAUSALITY_INPUT, 541 | COSIM_VARIABLE_CAUSALITY_PARAMETER, 542 | COSIM_VARIABLE_CAUSALITY_OUTPUT, 543 | COSIM_VARIABLE_CAUSALITY_CALCULATEDPARAMETER, 544 | COSIM_VARIABLE_CAUSALITY_LOCAL, 545 | COSIM_VARIABLE_CAUSALITY_INDEPENDENT 546 | } cosim_variable_causality; 547 | 548 | /// Variable variabilities. 549 | typedef enum 550 | { 551 | COSIM_VARIABLE_VARIABILITY_CONSTANT, 552 | COSIM_VARIABLE_VARIABILITY_FIXED, 553 | COSIM_VARIABLE_VARIABILITY_TUNABLE, 554 | COSIM_VARIABLE_VARIABILITY_DISCRETE, 555 | COSIM_VARIABLE_VARIABILITY_CONTINUOUS 556 | } cosim_variable_variability; 557 | 558 | /// A struct containing metadata for a variable. 559 | typedef struct 560 | { 561 | /// The name of the variable. 562 | char name[SLAVE_NAME_MAX_SIZE]; 563 | /// The value reference. 564 | cosim_value_reference reference; 565 | /// The variable type. 566 | cosim_variable_type type; 567 | /// The variable causality. 568 | cosim_variable_causality causality; 569 | /// The variable variability. 570 | cosim_variable_variability variability; 571 | } cosim_variable_description; 572 | 573 | /// Returns the number of variables for a slave which has been added to an execution, or -1 on error. 574 | int cosim_slave_get_num_variables(cosim_execution* execution, cosim_slave_index slave); 575 | 576 | /** 577 | * Returns variable metadata for a slave. 578 | * 579 | * \param [in] execution 580 | * The execution which the slave has been added to. 581 | * \param [in] slave 582 | * The index of the slave. 583 | * \param [out] variables 584 | * A pointer to an array of length `numVariables` which will be filled with actual `cosim_variable_description` values. 585 | * \param [in] numVariables 586 | * The length of the `variables` array. 587 | * 588 | * \returns 589 | * The number of variables written to `variables` array or -1 on error. 590 | */ 591 | int cosim_slave_get_variables(cosim_execution* execution, cosim_slave_index slave, cosim_variable_description variables[], size_t numVariables); 592 | 593 | /// Returns the number of variables in the execution that currently has an active modifier (all slaves). 594 | int cosim_get_num_modified_variables(cosim_execution* execution); 595 | 596 | /// A struct containing information about a slave which has been added to an execution. 597 | typedef struct 598 | { 599 | /// The slave instance name. 600 | char name[SLAVE_NAME_MAX_SIZE]; 601 | /// The slave's unique index in the exeuction. 602 | cosim_slave_index index; 603 | } cosim_slave_info; 604 | 605 | /// A struct containing variable information. 606 | typedef struct 607 | { 608 | /// The index of the slave containing the variable. 609 | cosim_slave_index slave_index; 610 | /// The type of the variable. 611 | cosim_variable_type type; 612 | /// The variable's value reference. 613 | cosim_value_reference value_reference; 614 | } cosim_variable_id; 615 | 616 | 617 | /// Returns the number of slaves which have been added to an execution. 618 | size_t cosim_execution_get_num_slaves(cosim_execution* execution); 619 | 620 | /** 621 | * Returns slave infos. 622 | * 623 | * \param [in] execution 624 | * The execution to get slave infos from. 625 | * \param [out] infos 626 | * A pointer to an array of length `num_slaves` which will be filled with actual `slave_info` values. 627 | * \param [in] numSlaves 628 | * The length of the `infos` array. 629 | * 630 | * \returns 631 | * 0 on success and -1 on error. 632 | */ 633 | int cosim_execution_get_slave_infos(cosim_execution* execution, cosim_slave_info infos[], size_t numSlaves); 634 | 635 | 636 | // Observer 637 | struct cosim_observer_s; 638 | 639 | /// An opaque object which contains the state for an observer. 640 | typedef struct cosim_observer_s cosim_observer; 641 | 642 | // Manipulator 643 | struct cosim_manipulator_s; 644 | 645 | /// An opaque object which contains the state for a manipulator. 646 | typedef struct cosim_manipulator_s cosim_manipulator; 647 | 648 | 649 | /** 650 | * Sets the values of real variables for one slave. 651 | * 652 | * \param [in] manipulator 653 | * The manipulator. 654 | * \param [in] slaveIndex 655 | * The slave. 656 | * \param [in] variables 657 | * A pointer to an array of length `nv` that contains the (slave-specific) 658 | * value references of variables to set. 659 | * \param [in] nv 660 | * The length of the `variables` and `values` arrays. 661 | * \param [out] values 662 | * A pointer to an array of length `nv` with the 663 | * values of the variables specified in `variables`, in the same order. 664 | * 665 | * \returns 666 | * 0 on success and -1 on error. 667 | */ 668 | int cosim_manipulator_slave_set_real( 669 | cosim_manipulator* manipulator, 670 | cosim_slave_index slaveIndex, 671 | const cosim_value_reference variables[], 672 | size_t nv, 673 | const double values[]); 674 | 675 | /** 676 | * Sets the values of integer variables for one slave. 677 | * 678 | * \param [in] manipulator 679 | * The manipulator. 680 | * \param [in] slaveIndex 681 | * The slave. 682 | * \param [in] variables 683 | * A pointer to an array of length `nv` that contains the (slave-specific) 684 | * value references of variables to set. 685 | * \param [in] nv 686 | * The length of the `variables` and `values` arrays. 687 | * \param [out] values 688 | * A pointer to an array of length `nv` with the 689 | * values of the variables specified in `variables`, in the same order. 690 | * 691 | * \returns 692 | * 0 on success and -1 on error. 693 | */ 694 | int cosim_manipulator_slave_set_integer( 695 | cosim_manipulator* manipulator, 696 | cosim_slave_index slaveIndex, 697 | const cosim_value_reference variables[], 698 | size_t nv, 699 | const int values[]); 700 | 701 | /** 702 | * Sets the values of boolean variables for one slave. 703 | * 704 | * \param [in] manipulator 705 | * The manipulator. 706 | * \param [in] slaveIndex 707 | * The slave. 708 | * \param [in] variables 709 | * A pointer to an array of length `nv` that contains the (slave-specific) 710 | * value references of variables to set. 711 | * \param [in] nv 712 | * The length of the `variables` and `values` arrays. 713 | * \param [out] values 714 | * A pointer to an array of length `nv` with the 715 | * values of the variables specified in `variables`, in the same order. 716 | * 717 | * \returns 718 | * 0 on success and -1 on error. 719 | */ 720 | int cosim_manipulator_slave_set_boolean( 721 | cosim_manipulator* manipulator, 722 | cosim_slave_index slaveIndex, 723 | const cosim_value_reference variables[], 724 | size_t nv, 725 | const bool values[]); 726 | 727 | /** 728 | * Sets the values of string variables for one slave. 729 | * 730 | * \param [in] manipulator 731 | * The manipulator. 732 | * \param [in] slaveIndex 733 | * The slave. 734 | * \param [in] variables 735 | * A pointer to an array of length `nv` that contains the (slave-specific) 736 | * value references of variables to set. 737 | * \param [in] nv 738 | * The length of the `variables` and `values` arrays. 739 | * \param [out] values 740 | * A pointer to an array of length `nv` with the 741 | * values of the variables specified in `variables`, in the same order. 742 | * 743 | * \returns 744 | * 0 on success and -1 on error. 745 | */ 746 | int cosim_manipulator_slave_set_string( 747 | cosim_manipulator* manipulator, 748 | cosim_slave_index slaveIndex, 749 | const cosim_value_reference variables[], 750 | size_t nv, 751 | const char* values[]); 752 | 753 | /** 754 | * Resets any previously overridden variable values of a certain type for one slave. 755 | * 756 | * \param [in] manipulator 757 | * The manipulator. 758 | * \param [in] slaveIndex 759 | * The slave. 760 | * \param [in] type 761 | * The variable type. 762 | * \param [in] variables 763 | * A pointer to an array of length `nv` that contains the (slave-specific) 764 | * value references of variables to reset. 765 | * \param [in] nv 766 | * The length of the `variables` array. 767 | * 768 | * \returns 769 | * 0 on success and -1 on error. 770 | */ 771 | int cosim_manipulator_slave_reset( 772 | cosim_manipulator* manipulator, 773 | cosim_slave_index slaveIndex, 774 | cosim_variable_type type, 775 | const cosim_value_reference variables[], 776 | size_t nv); 777 | 778 | /** 779 | * Retrieves the values of real variables for one slave. 780 | * 781 | * \param [in] observer 782 | * The observer. 783 | * \param [in] slave 784 | * The slave. 785 | * \param [in] variables 786 | * A pointer to an array of length `nv` that contains the (slave-specific) 787 | * value references of variables to retrieve. 788 | * \param [in] nv 789 | * The length of the `variables` and `values` arrays. 790 | * \param [out] values 791 | * A pointer to an array of length `nv` which will be filled with the 792 | * values of the variables specified in `variables`, in the same order. 793 | * 794 | * \returns 795 | * 0 on success and -1 on error. 796 | */ 797 | int cosim_observer_slave_get_real( 798 | cosim_observer* observer, 799 | cosim_slave_index slave, 800 | const cosim_value_reference variables[], 801 | size_t nv, 802 | double values[]); 803 | 804 | /** 805 | * Retrieves the values of integer variables for one slave. 806 | * 807 | * \param [in] observer 808 | * The observer. 809 | * \param [in] slave 810 | * The slave index. 811 | * \param [in] variables 812 | * A pointer to an array of length `nv` that contains the (slave-specific) 813 | * value references of variables to retrieve. 814 | * \param [in] nv 815 | * The length of the `variables` and `values` arrays. 816 | * \param [out] values 817 | * A pointer to an array of length `nv` which will be filled with the 818 | * values of the variables specified in `variables`, in the same order. 819 | * 820 | * \returns 821 | * 0 on success and -1 on error. 822 | */ 823 | int cosim_observer_slave_get_integer( 824 | cosim_observer* observer, 825 | cosim_slave_index slave, 826 | const cosim_value_reference variables[], 827 | size_t nv, 828 | int values[]); 829 | 830 | /** 831 | * Retrieves the values of boolean variables for one slave. 832 | * 833 | * \param [in] observer 834 | * The observer. 835 | * \param [in] slave 836 | * The slave index. 837 | * \param [in] variables 838 | * A pointer to an array of length `nv` that contains the (slave-specific) 839 | * value references of variables to retrieve. 840 | * \param [in] nv 841 | * The length of the `variables` and `values` arrays. 842 | * \param [out] values 843 | * A pointer to an array of length `nv` which will be filled with the 844 | * values of the variables specified in `variables`, in the same order. 845 | * 846 | * \returns 847 | * 0 on success and -1 on error. 848 | */ 849 | int cosim_observer_slave_get_boolean( 850 | cosim_observer* observer, 851 | cosim_slave_index slave, 852 | const cosim_value_reference variables[], 853 | size_t nv, 854 | bool values[]); 855 | 856 | /** 857 | * Retrieves the values of string variables for one slave. 858 | * 859 | * \param [in] observer 860 | * The observer. 861 | * \param [in] slave 862 | * The slave index. 863 | * \param [in] variables 864 | * A pointer to an array of length `nv` that contains the (slave-specific) 865 | * value references of variables to retrieve. 866 | * \param [in] nv 867 | * The length of the `variables` and `values` arrays. 868 | * \param [out] values 869 | * A pointer to an array of length `nv` which will be filled with pointers 870 | * to the values of the variables specified in `variables`, in the same order. 871 | * The pointers are valid until the next call to `cosim_observer_slave_get_string()`. 872 | * 873 | * \returns 874 | * 0 on success and -1 on error. 875 | */ 876 | int cosim_observer_slave_get_string( 877 | cosim_observer* observer, 878 | cosim_slave_index slave, 879 | const cosim_value_reference variables[], 880 | size_t nv, 881 | const char* values[]); 882 | 883 | /** 884 | * Retrieves a series of observed values, step numbers and times for a real variable. 885 | * 886 | * \param [in] observer the observer 887 | * \param [in] slave index of the slave 888 | * \param [in] valueReference the value reference 889 | * \param [in] fromStep the step number to start from 890 | * \param [in] nSamples the number of samples to read 891 | * \param [out] values the series of observed values 892 | * \param [out] steps the corresponding step numbers 893 | * \param [out] times the corresponding simulation times 894 | * 895 | * \returns 896 | * The number of samples actually read, which may be smaller than `nSamples`. 897 | */ 898 | int64_t cosim_observer_slave_get_real_samples( 899 | cosim_observer* observer, 900 | cosim_slave_index slave, 901 | cosim_value_reference valueReference, 902 | cosim_step_number fromStep, 903 | size_t nSamples, 904 | double values[], 905 | cosim_step_number steps[], 906 | cosim_time_point times[]); 907 | 908 | /** 909 | * Retrieves a series of observed values, step numbers and times for an integer variable. 910 | * 911 | * \param [in] observer the observer 912 | * \param [in] slave index of the slave 913 | * \param [in] valueReference the value reference 914 | * \param [in] fromStep the step number to start from 915 | * \param [in] nSamples the number of samples to read 916 | * \param [out] values the series of observed values 917 | * \param [out] steps the corresponding step numbers 918 | * \param [out] times the corresponding simulation times 919 | * 920 | * \returns 921 | * The number of samples actually read, which may be smaller than `nSamples`. 922 | */ 923 | int64_t cosim_observer_slave_get_integer_samples( 924 | cosim_observer* observer, 925 | cosim_slave_index slave, 926 | cosim_value_reference valueReference, 927 | cosim_step_number fromStep, 928 | size_t nSamples, 929 | int values[], 930 | cosim_step_number steps[], 931 | cosim_time_point times[]); 932 | 933 | /** 934 | * Retrieves two time-synchronized series of observed values for two real variables. 935 | * 936 | * \param [in] observer the observer 937 | * \param [in] slave1 index of the first slave 938 | * \param [in] valueReference1 the first value reference 939 | * \param [in] slave2 index of the second slave 940 | * \param [in] valueReference2 the second value reference 941 | * \param [in] fromStep the step number to start from 942 | * \param [in] nSamples the number of samples to read 943 | * \param [out] values1 the first series of observed values 944 | * \param [out] values2 the second series of observed values 945 | * 946 | * \returns 947 | * The number of samples actually read, which may be smaller than `nSamples`. 948 | */ 949 | int64_t cosim_observer_slave_get_real_synchronized_series( 950 | cosim_observer* observer, 951 | cosim_slave_index slave1, 952 | cosim_value_reference valueReference1, 953 | cosim_slave_index slave2, 954 | cosim_value_reference valueReference2, 955 | cosim_step_number fromStep, 956 | size_t nSamples, 957 | double values1[], 958 | double values2[]); 959 | 960 | /** 961 | * Retrieves the step numbers for a range given by a duration. 962 | * 963 | * Helper function which can be used in conjunction with `cosim_observer_slave_get_xxx_samples()` 964 | * when it is desired to retrieve the latest available samples given a certain duration. 965 | * 966 | * \note 967 | * It is assumed that `steps` has a length of 2. 968 | * 969 | * \param [in] observer the observer 970 | * \param [in] slave index of the slave 971 | * \param [in] duration the duration to get step numbers for 972 | * \param [out] steps the corresponding step numbers 973 | */ 974 | int cosim_observer_get_step_numbers_for_duration( 975 | cosim_observer* observer, 976 | cosim_slave_index slave, 977 | cosim_duration duration, 978 | cosim_step_number steps[]); 979 | 980 | /** 981 | * Retrieves the step numbers for a range given by two points in time. 982 | * 983 | * Helper function which can be used in conjunction with `cosim_observer_slave_get_xxx_samples()` 984 | * when it is desired to retrieve samples between two points in time. 985 | * 986 | * \note 987 | * It is assumed that `steps` has a length of 2. 988 | * 989 | * \param [in] observer the observer 990 | * \param [in] slave index of the simulator 991 | * \param [in] begin the start of the range 992 | * \param [in] end the end of the range 993 | * \param [out] steps the corresponding step numbers 994 | */ 995 | int cosim_observer_get_step_numbers( 996 | cosim_observer* observer, 997 | cosim_slave_index slave, 998 | cosim_time_point begin, 999 | cosim_time_point end, 1000 | cosim_step_number steps[]); 1001 | 1002 | /** 1003 | * Connects one real output variable to one real input variable. 1004 | * 1005 | * \param [in] execution 1006 | * The execution. 1007 | * \param [in] outputSlaveIndex 1008 | * The source slave. 1009 | * \param [in] outputValueReference 1010 | * The source variable. 1011 | * \param [in] inputSlaveIndex 1012 | * The destination slave. 1013 | * \param [in] inputValueReference 1014 | * The destination variable. 1015 | * 1016 | * \returns 1017 | * 0 on success and -1 on error. 1018 | */ 1019 | int cosim_execution_connect_real_variables( 1020 | cosim_execution* execution, 1021 | cosim_slave_index outputSlaveIndex, 1022 | cosim_value_reference outputValueReference, 1023 | cosim_slave_index inputSlaveIndex, 1024 | cosim_value_reference inputValueReference); 1025 | 1026 | /** 1027 | * Connects one integer output variable to one integer input variable. 1028 | * 1029 | * \param [in] execution 1030 | * The execution. 1031 | * \param [in] outputSlaveIndex 1032 | * The source slave. 1033 | * \param [in] outputValueReference 1034 | * The source variable. 1035 | * \param [in] inputSlaveIndex 1036 | * The destination slave. 1037 | * \param [in] inputValueReference 1038 | * The destination variable. 1039 | * 1040 | * \returns 1041 | * 0 on success and -1 on error. 1042 | */ 1043 | int cosim_execution_connect_integer_variables( 1044 | cosim_execution* execution, 1045 | cosim_slave_index outputSlaveIndex, 1046 | cosim_value_reference outputValueReference, 1047 | cosim_slave_index inputSlaveIndex, 1048 | cosim_value_reference inputValueReference); 1049 | 1050 | 1051 | /** 1052 | * Connects one boolean output variable to one boolean input variable. 1053 | * 1054 | * \param [in] execution 1055 | * The execution. 1056 | * \param [in] outputSlaveIndex 1057 | * The source slave. 1058 | * \param [in] outputValueReference 1059 | * The source variable. 1060 | * \param [in] inputSlaveIndex 1061 | * The destination slave. 1062 | * \param [in] inputValueReference 1063 | * The destination variable. 1064 | * 1065 | * \returns 1066 | * 0 on success and -1 on error. 1067 | */ 1068 | int cosim_execution_connect_boolean_variables( 1069 | cosim_execution* execution, 1070 | cosim_slave_index outputSlaveIndex, 1071 | cosim_value_reference outputValueReference, 1072 | cosim_slave_index inputSlaveIndex, 1073 | cosim_value_reference inputValueReference); 1074 | 1075 | 1076 | /** 1077 | * Connects one string output variable to one string input variable. 1078 | * 1079 | * \param [in] execution 1080 | * The execution. 1081 | * \param [in] outputSlaveIndex 1082 | * The source slave. 1083 | * \param [in] outputValueReference 1084 | * The source variable. 1085 | * \param [in] inputSlaveIndex 1086 | * The destination slave. 1087 | * \param [in] inputValueReference 1088 | * The destination variable. 1089 | * 1090 | * \returns 1091 | * 0 on success and -1 on error. 1092 | */ 1093 | int cosim_execution_connect_string_variables( 1094 | cosim_execution* execution, 1095 | cosim_slave_index outputSlaveIndex, 1096 | cosim_value_reference outputValueReference, 1097 | cosim_slave_index inputSlaveIndex, 1098 | cosim_value_reference inputValueReference); 1099 | /// Creates an observer which stores the last observed value for all variables. 1100 | cosim_observer* cosim_last_value_observer_create(); 1101 | 1102 | /** 1103 | * Creates an observer which logs variable values to file in csv format. 1104 | * 1105 | * @param logDir 1106 | * The directory where log files will be created. 1107 | * \returns 1108 | * The created observer. 1109 | */ 1110 | cosim_observer* cosim_file_observer_create(const char* logDir); 1111 | 1112 | /** 1113 | * Creates an observer which logs variable values to file in csv format. Variables to be logged 1114 | * are specified in the supplied log config xml file. 1115 | * 1116 | * @param logDir 1117 | * The directory where log files will be created. 1118 | * @param logConfigXml 1119 | * The path to the provided config xml file. 1120 | * \returns 1121 | * The created observer. 1122 | */ 1123 | cosim_observer* cosim_file_observer_create_from_cfg(const char* logDir, const char* logConfigXml); 1124 | 1125 | /** 1126 | * Creates an observer which buffers variable values in memory. The buffer size is set to keep 10000 variable values in memory. 1127 | * 1128 | * To start observing a variable, `cosim_observer_start_observing()` must be called. 1129 | */ 1130 | cosim_observer* cosim_time_series_observer_create(); 1131 | 1132 | /** 1133 | * Creates an observer which buffers up to `bufferSize` variable values in memory. 1134 | * 1135 | * To start observing a variable, `cosim_observer_start_observing()` must be called. 1136 | */ 1137 | cosim_observer* cosim_buffered_time_series_observer_create(size_t bufferSize); 1138 | 1139 | /// Start observing a variable with a `time_series_observer`. 1140 | int cosim_observer_start_observing(cosim_observer* observer, cosim_slave_index slave, cosim_variable_type type, cosim_value_reference reference); 1141 | 1142 | /// Stop observing a variable with a `time_series_observer`. 1143 | int cosim_observer_stop_observing(cosim_observer* observer, cosim_slave_index slave, cosim_variable_type type, cosim_value_reference reference); 1144 | 1145 | /// Destroys an observer 1146 | int cosim_observer_destroy(cosim_observer* observer); 1147 | 1148 | 1149 | /** 1150 | * Add an observer to an execution. 1151 | * 1152 | * \param [in] execution 1153 | * The execution. 1154 | * \param [in] observer 1155 | * A pointer to an observer, which may not be null. The observer may 1156 | * not previously have been added to any execution. 1157 | * 1158 | * \returns 1159 | * 0 on success and -1 on error. 1160 | */ 1161 | int cosim_execution_add_observer( 1162 | cosim_execution* execution, 1163 | cosim_observer* observer); 1164 | 1165 | /// Creates a manipulator for overriding variable values 1166 | cosim_manipulator* cosim_override_manipulator_create(); 1167 | 1168 | /** 1169 | * Add a manipulator to an execution. 1170 | * 1171 | * \param [in] execution 1172 | * The execution. 1173 | * \param [in] manipulator 1174 | * A pointer to a manipulator, which may not be null. The manipulator may 1175 | * not previously have been added to any execution. 1176 | * 1177 | * \returns 1178 | * 0 on success and -1 on error. 1179 | */ 1180 | int cosim_execution_add_manipulator( 1181 | cosim_execution* execution, 1182 | cosim_manipulator* manipulator); 1183 | 1184 | /// Destroys a manipulator 1185 | int cosim_manipulator_destroy(cosim_manipulator* manipulator); 1186 | 1187 | /// Creates a manipulator for running scenarios. 1188 | cosim_manipulator* cosim_scenario_manager_create(); 1189 | 1190 | /// Loads and executes a scenario from file. 1191 | int cosim_execution_load_scenario( 1192 | cosim_execution* execution, 1193 | cosim_manipulator* manipulator, 1194 | const char* scenarioFile); 1195 | 1196 | /// Checks if a scenario is running 1197 | int cosim_scenario_is_running(cosim_manipulator* manipulator); 1198 | 1199 | /// Aborts the execution of a running scenario 1200 | int cosim_scenario_abort(cosim_manipulator* manipulator); 1201 | 1202 | /** 1203 | * Retrieves a list of the currently modified variables in the simulation. 1204 | * 1205 | * \param [in] execution 1206 | * The execution. 1207 | * \param [out] ids 1208 | * A list of cosim_variable_id structs to contain the variable information. 1209 | * \param [in] numVariables 1210 | * The length of the `ids` array. 1211 | * 1212 | * \returns 1213 | * 0 on success and -1 on error. 1214 | */ 1215 | int cosim_get_modified_variables(cosim_execution* execution, cosim_variable_id ids[], size_t numVariables); 1216 | 1217 | 1218 | /// Severity levels for log messages. 1219 | typedef enum 1220 | { 1221 | COSIM_LOG_SEVERITY_TRACE, 1222 | COSIM_LOG_SEVERITY_DEBUG, 1223 | COSIM_LOG_SEVERITY_INFO, 1224 | COSIM_LOG_SEVERITY_WARNING, 1225 | COSIM_LOG_SEVERITY_ERROR, 1226 | COSIM_LOG_SEVERITY_FATAL 1227 | } cosim_log_severity_level; 1228 | 1229 | 1230 | /** 1231 | * Configures simple console logging. 1232 | * 1233 | * Note that the library may produce log messages before this function is 1234 | * called, but then it uses the default or existing settings of the underlying 1235 | * logging framework (Boost.Log). 1236 | * 1237 | * \returns 1238 | * 0 on success and -1 on error. 1239 | */ 1240 | int cosim_log_setup_simple_console_logging(); 1241 | 1242 | 1243 | /** 1244 | * Installs a global severity level filter for log messages. 1245 | * 1246 | * This function sets up a log message filter which ensures that only messages 1247 | * whose severity level is at least `level` will be printed. 1248 | * 1249 | * \param [in] level 1250 | * The minimum visible severity level. 1251 | */ 1252 | void cosim_log_set_output_level(cosim_log_severity_level level); 1253 | 1254 | 1255 | /// Software version 1256 | typedef struct 1257 | { 1258 | int major; 1259 | int minor; 1260 | int patch; 1261 | } cosim_version; 1262 | 1263 | 1264 | /// Returns the version of the wrapped libcosim C++ library. 1265 | cosim_version cosim_libcosim_version(); 1266 | 1267 | /// Returns the libcosimc version. 1268 | cosim_version cosim_libcosimc_version(); 1269 | 1270 | 1271 | #ifdef __cplusplus 1272 | } // extern(C) 1273 | #endif 1274 | 1275 | #endif // header guard 1276 | -------------------------------------------------------------------------------- /src/lib_info.cpp.in: -------------------------------------------------------------------------------- 1 | #include "cosim.h" 2 | 3 | #include 4 | 5 | 6 | cosim_version cosim_libcosim_version() 7 | { 8 | const auto v = cosim::library_version(); 9 | return { v.major, v.minor, v.patch }; 10 | } 11 | 12 | cosim_version cosim_libcosimc_version() 13 | { 14 | // clang-format off 15 | return { @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@ }; 16 | // clang-format on 17 | } 18 | -------------------------------------------------------------------------------- /tests/connections_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef _WINDOWS 8 | # include 9 | #else 10 | # include 11 | # define Sleep(x) usleep((x)*1000) 12 | #endif 13 | 14 | void print_last_error() 15 | { 16 | fprintf( 17 | stderr, 18 | "Error code %d: %s\n", 19 | cosim_last_error_code(), cosim_last_error_message()); 20 | } 21 | 22 | int main() 23 | { 24 | cosim_log_setup_simple_console_logging(); 25 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 26 | 27 | int exitCode = 0; 28 | const char* dataDir = getenv("TEST_DATA_DIR"); 29 | char fmuPath[1024]; 30 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 31 | snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 32 | 33 | cosim_execution* execution = NULL; 34 | cosim_slave* slave1 = NULL; 35 | cosim_slave* slave2 = NULL; 36 | cosim_observer* observer = NULL; 37 | cosim_manipulator* manipulator = NULL; 38 | 39 | execution = cosim_execution_create(0, nanoStepSize); 40 | if (!execution) { goto Lerror; } 41 | 42 | slave1 = cosim_local_slave_create(fmuPath, "slave1"); 43 | if (!slave1) { goto Lerror; } 44 | 45 | slave2 = cosim_local_slave_create(fmuPath, "slave2"); 46 | if (!slave2) { goto Lerror; } 47 | 48 | observer = cosim_last_value_observer_create(); 49 | if (!observer) { goto Lerror; } 50 | 51 | cosim_slave_index slaveIndex1 = cosim_execution_add_slave(execution, slave1); 52 | if (slaveIndex1 < 0) { goto Lerror; } 53 | 54 | cosim_slave_index slaveIndex2 = cosim_execution_add_slave(execution, slave2); 55 | if (slaveIndex2 < 0) { goto Lerror; } 56 | 57 | int rc = cosim_execution_add_observer(execution, observer); 58 | if (rc < 0) { goto Lerror; } 59 | 60 | rc = cosim_execution_connect_real_variables(execution, slaveIndex1, 0, slaveIndex2, 0); 61 | if (rc < 0) { goto Lerror; } 62 | 63 | rc = cosim_execution_connect_integer_variables(execution, slaveIndex1, 0, slaveIndex2, 0); 64 | if (rc < 0) { goto Lerror; } 65 | 66 | // Test that this fails 67 | rc = cosim_execution_connect_integer_variables(execution, slaveIndex1, 1, slaveIndex2, 1); 68 | if (rc != -1) { 69 | fprintf(stderr, "Expected to fail when connecting nonexistent variables"); 70 | goto Lfailure; 71 | } 72 | 73 | manipulator = cosim_override_manipulator_create(); 74 | if (!manipulator) { goto Lerror; } 75 | 76 | rc = cosim_execution_add_manipulator(execution, manipulator); 77 | if (rc < 0) { goto Lerror; } 78 | 79 | cosim_value_reference realInVar = 0; 80 | const double realInVal = 5.0; 81 | rc = cosim_manipulator_slave_set_real(manipulator, slaveIndex1, &realInVar, 1, &realInVal); 82 | if (rc < 0) { goto Lerror; } 83 | 84 | cosim_value_reference intInVar = 0; 85 | const int intInVal = 42; 86 | rc = cosim_manipulator_slave_set_integer(manipulator, slaveIndex1, &intInVar, 1, &intInVal); 87 | if (rc < 0) { goto Lerror; } 88 | 89 | rc = cosim_execution_step(execution, 10); 90 | if (rc < 0) { goto Lerror; } 91 | 92 | cosim_value_reference realOutVar = 0; 93 | double realOutVal = -1.0; 94 | rc = cosim_observer_slave_get_real(observer, slaveIndex2, &realOutVar, 1, &realOutVal); 95 | if (rc < 0) { goto Lerror; } 96 | 97 | cosim_value_reference intOutVar = 0; 98 | int intOutVal = -1; 99 | rc = cosim_observer_slave_get_integer(observer, slaveIndex2, &intOutVar, 1, &intOutVal); 100 | if (rc < 0) { goto Lerror; } 101 | 102 | if (realOutVal != 5.0) { 103 | fprintf(stderr, "Expected value 5.0, got %f\n", realOutVal); 104 | goto Lfailure; 105 | } 106 | if (intOutVal != 42) { 107 | fprintf(stderr, "Expected value 42, got %i\n", intOutVal); 108 | goto Lfailure; 109 | } 110 | 111 | goto Lcleanup; 112 | 113 | Lerror: 114 | print_last_error(); 115 | 116 | Lfailure: 117 | exitCode = 1; 118 | 119 | Lcleanup: 120 | cosim_manipulator_destroy(manipulator); 121 | cosim_observer_destroy(observer); 122 | cosim_local_slave_destroy(slave2); 123 | cosim_local_slave_destroy(slave1); 124 | cosim_execution_destroy(execution); 125 | 126 | return exitCode; 127 | } 128 | -------------------------------------------------------------------------------- /tests/data/fmi1/identity.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-simulation-platform/libcosimc/d4cc1722d237e7f1fa03e67e338d1270a5777e05/tests/data/fmi1/identity.fmu -------------------------------------------------------------------------------- /tests/data/fmi2/fail.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-simulation-platform/libcosimc/d4cc1722d237e7f1fa03e67e338d1270a5777e05/tests/data/fmi2/fail.fmu -------------------------------------------------------------------------------- /tests/data/fmi2/quarter_truck/Chassis.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-simulation-platform/libcosimc/d4cc1722d237e7f1fa03e67e338d1270a5777e05/tests/data/fmi2/quarter_truck/Chassis.fmu -------------------------------------------------------------------------------- /tests/data/fmi2/quarter_truck/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2025 SINTEF Nordvest 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /tests/data/fmi2/quarter_truck/LogConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/data/fmi2/quarter_truck/OspSystemStructure.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0.0001 4 | ecco 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 0.99 45 | 0.0001 46 | 0.00001 47 | 0.01 48 | 0.2 49 | 1.5 50 | 0.2 51 | 0.15 52 | 1e-6 53 | 1e-6 54 | 55 | 56 | -------------------------------------------------------------------------------- /tests/data/fmi2/quarter_truck/README.md: -------------------------------------------------------------------------------- 1 | # FMI 2.0 test FMUs 2 | 3 | | Name | Origin | License | 4 | | ---------------------- | -------------- | ----------------------------- | 5 | | `Chassis.fmu` | OSP | [MIT](LICENSE) | 6 | | `Wheel.fmu` | OSP | [MIT](LICENSE) | 7 | 8 | [OSP demo-cases]: https://github.com/open-simulation-platform/demo-cases 9 | -------------------------------------------------------------------------------- /tests/data/fmi2/quarter_truck/Wheel.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-simulation-platform/libcosimc/d4cc1722d237e7f1fa03e67e338d1270a5777e05/tests/data/fmi2/quarter_truck/Wheel.fmu -------------------------------------------------------------------------------- /tests/data/fmi2/quarter_truck/chassis_OspModelDescription.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/data/fmi2/quarter_truck/wheel_OspModelDescription.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/data/msmi/CraneController_OspModelDescription.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/data/msmi/OspSystemStructure.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 0.0 8 | 1e-4 9 | fixedStep 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 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 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /tests/data/ssp/demo/CraneController.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-simulation-platform/libcosimc/d4cc1722d237e7f1fa03e67e338d1270a5777e05/tests/data/ssp/demo/CraneController.fmu -------------------------------------------------------------------------------- /tests/data/ssp/demo/KnuckleBoomCrane.fmu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-simulation-platform/libcosimc/d4cc1722d237e7f1fa03e67e338d1270a5777e05/tests/data/ssp/demo/KnuckleBoomCrane.fmu -------------------------------------------------------------------------------- /tests/data/ssp/demo/KnuckleBoomCrane_OspModelDescription.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/data/ssp/demo/SystemStructure.ssd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 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 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /tests/data/ssp/demo/no_algorithm_element/SystemStructure.ssd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 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 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /tests/ecco_algorithm_multi_bond_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | #define MAX_NUMBER_OF_SLAVES 10 16 | 17 | void print_last_error() 18 | { 19 | fprintf( 20 | stderr, 21 | "Error code %d: %s\n", 22 | cosim_last_error_code(), cosim_last_error_message()); 23 | } 24 | 25 | int main() 26 | { 27 | cosim_log_setup_simple_console_logging(); 28 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 29 | 30 | int exitCode = 0; 31 | const char* dataDir = getenv("TEST_DATA_DIR"); 32 | char chassisFmuPath[1024]; 33 | char wheelFmuPath[1024]; 34 | char logConfigPath[1024]; 35 | int64_t toTimePoint = (int64_t)(4 * 1e9); 36 | snprintf(chassisFmuPath, sizeof chassisFmuPath, "%s/fmi2/quarter_truck/Chassis.fmu", dataDir); 37 | snprintf(wheelFmuPath, sizeof wheelFmuPath, "%s/fmi2/quarter_truck/Wheel.fmu", dataDir); 38 | snprintf(logConfigPath, sizeof logConfigPath, "%s/fmi2/quarter_truck/LogConfig.xml", dataDir); 39 | 40 | cosim_execution* execution = NULL; 41 | cosim_slave* chassis = NULL; 42 | cosim_slave* wheel = NULL; 43 | cosim_observer* observer = NULL; 44 | double* cvo = NULL; 45 | double* wvi = NULL; 46 | double* cfi = NULL; 47 | double* wfo = NULL; 48 | cosim_time_point* times = NULL; 49 | cosim_step_number* steps = NULL; 50 | double* diffs = NULL; 51 | 52 | cosim_algorithm* ecco_algorithm = cosim_ecco_algorithm_create( 53 | 0.8, 54 | 1e-4, 55 | 1e-4, 56 | 0.01, 57 | 0.2, 58 | 1.5, 59 | 1e-4, 60 | 1e-4, 61 | 0.2, 62 | 0.15); 63 | 64 | execution = cosim_execution_create_with_algorithm(0, ecco_algorithm); 65 | if (!execution) {goto Lerror;} 66 | 67 | chassis = cosim_local_slave_create(chassisFmuPath, "chassis"); 68 | if (!chassis) { goto Lerror; } 69 | 70 | wheel = cosim_local_slave_create(wheelFmuPath, "wheel"); 71 | if (!wheel) { goto Lerror; } 72 | 73 | // observer = cosim_time_series_observer_create(); 74 | observer = cosim_buffered_time_series_observer_create(500000); 75 | if (!observer) { goto Lerror; } 76 | 77 | int rc = cosim_execution_add_observer(execution, observer); 78 | if (rc < 0) { goto Lerror; } 79 | 80 | cosim_slave_index chassisIndex = cosim_execution_add_slave(execution, chassis); 81 | if (chassisIndex < 0) { goto Lerror; } 82 | 83 | cosim_slave_index wheelIndex = cosim_execution_add_slave(execution, wheel); 84 | if (wheelIndex < 0) { goto Lerror; } 85 | 86 | // IO connections 87 | cosim_value_reference chassisVelOut = 23; 88 | cosim_value_reference chassisFIn = 4; 89 | cosim_value_reference wheelFOut = 15; 90 | cosim_value_reference wheelVelIn = 7; 91 | 92 | rc = cosim_execution_connect_real_variables(execution, chassisIndex, chassisVelOut, wheelIndex, wheelVelIn); 93 | if (rc < 0) { goto Lerror; } 94 | rc = cosim_execution_connect_real_variables(execution, wheelIndex, wheelFOut, chassisIndex, chassisFIn); 95 | if (rc < 0) { goto Lerror; } 96 | 97 | // Power bond connections 98 | cosim_ecco_add_power_bond(ecco_algorithm, chassisIndex, chassisVelOut, chassisFIn, wheelIndex, wheelFOut, wheelVelIn); 99 | 100 | // Initial values 101 | rc = cosim_execution_set_real_initial_value(execution, chassisIndex, 8, 400); // mass 102 | if (rc < 0) { goto Lerror; } 103 | rc = cosim_execution_set_string_initial_value(execution, chassisIndex, 1, "Euler"); // solverType 104 | if (rc < 0) { goto Lerror; } 105 | rc = cosim_execution_set_real_initial_value(execution, chassisIndex, 21, 1e-5); // timeStep 106 | if (rc < 0) { goto Lerror; } 107 | 108 | rc = cosim_execution_set_real_initial_value(execution, wheelIndex, 13, 40); // mass 109 | if (rc < 0) { goto Lerror; } 110 | rc = cosim_execution_set_string_initial_value(execution, wheelIndex, 1, "Euler"); // solverType 111 | if (rc < 0) { goto Lerror; } 112 | rc = cosim_execution_set_real_initial_value(execution, wheelIndex, 28, 1e-5); // timeStep 113 | if (rc < 0) { goto Lerror; } 114 | 115 | // Start observers 116 | rc = cosim_observer_start_observing(observer, wheelIndex, COSIM_VARIABLE_TYPE_REAL, wheelVelIn); 117 | if (rc < 0) { goto Lerror; } 118 | rc = cosim_observer_start_observing(observer, chassisIndex, COSIM_VARIABLE_TYPE_REAL, chassisVelOut); 119 | if (rc < 0) { goto Lerror; } 120 | rc = cosim_observer_start_observing(observer, wheelIndex, COSIM_VARIABLE_TYPE_REAL, wheelFOut); 121 | if (rc < 0) { goto Lerror; } 122 | rc = cosim_observer_start_observing(observer, chassisIndex, COSIM_VARIABLE_TYPE_REAL, chassisFIn); 123 | if (rc < 0) { goto Lerror; } 124 | 125 | rc = cosim_execution_simulate_until(execution, toTimePoint); 126 | if (rc < 0) { goto Lerror; } 127 | 128 | cosim_step_number fromStep = 1; 129 | cosim_step_number stepNumbers[3]; 130 | 131 | rc = cosim_observer_get_step_numbers(observer, chassisIndex, 0, toTimePoint, stepNumbers); 132 | if (rc < 0) { goto Lerror; } 133 | 134 | const size_t nSamples = stepNumbers[1] - stepNumbers[0]; 135 | steps = (cosim_step_number*)malloc(nSamples * sizeof(cosim_step_number)); 136 | times = (cosim_time_point*)malloc(nSamples * sizeof(cosim_time_point)); 137 | cvo = (double*)malloc(nSamples * sizeof(double)); 138 | cfi = (double*)malloc(nSamples * sizeof(double)); 139 | wvi = (double*)malloc(nSamples * sizeof(double)); 140 | wfo = (double*)malloc(nSamples * sizeof(double)); 141 | int64_t samplesRead; 142 | 143 | samplesRead = cosim_observer_slave_get_real_samples(observer, chassisIndex, chassisVelOut, fromStep, nSamples, cvo, steps, times); 144 | if (samplesRead < 0) { goto Lerror; } 145 | samplesRead = cosim_observer_slave_get_real_samples(observer, chassisIndex, chassisFIn, fromStep, nSamples, cfi, steps, times); 146 | if (samplesRead < 0) { goto Lerror; } 147 | samplesRead = cosim_observer_slave_get_real_samples(observer, wheelIndex, wheelVelIn, fromStep, nSamples, wvi, steps, times); 148 | if (samplesRead < 0) { goto Lerror; } 149 | samplesRead = cosim_observer_slave_get_real_samples(observer, wheelIndex, wheelFOut, fromStep, nSamples, wfo, steps, times); 150 | if (samplesRead < 0) { goto Lerror; } 151 | 152 | const float threshold = 1e-2f; 153 | diffs = (double*)malloc(nSamples * sizeof(double)); 154 | size_t ptr = 0; 155 | for (size_t i = 1; i < nSamples; ++i) { 156 | diffs[ptr++] = fabs((cvo[i] * cfi[i]) - (wvi[i] * wfo[i]));; 157 | } 158 | 159 | const size_t offset = 100; 160 | for (size_t i = (nSamples > offset ? nSamples - offset : 0); i < nSamples; i++) { 161 | if (diffs[i] > threshold) { 162 | fprintf(stderr, "Power bond mismatch at sample %zu: %f\n", i, diffs[i]); 163 | goto Lfailure; 164 | } 165 | } 166 | 167 | goto Lcleanup; 168 | 169 | Lerror: 170 | print_last_error(); 171 | 172 | Lfailure: 173 | exitCode = 1; 174 | 175 | Lcleanup: 176 | cosim_observer_destroy(observer); 177 | cosim_local_slave_destroy(chassis); 178 | cosim_local_slave_destroy(wheel); 179 | cosim_execution_destroy(execution); 180 | free(cvo); 181 | free(wvi); 182 | free(cfi); 183 | free(wfo); 184 | free(times); 185 | free(diffs); 186 | 187 | return exitCode; 188 | } 189 | -------------------------------------------------------------------------------- /tests/execution_from_osp_config_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | #define MAX_NUMBER_OF_SLAVES 10 16 | 17 | void print_last_error() 18 | { 19 | fprintf( 20 | stderr, 21 | "Error code %d: %s\n", 22 | cosim_last_error_code(), cosim_last_error_message()); 23 | } 24 | 25 | int main() 26 | { 27 | cosim_log_setup_simple_console_logging(); 28 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 29 | 30 | int exitCode = 0; 31 | cosim_execution* execution = NULL; 32 | cosim_observer* observer = NULL; 33 | 34 | const char* dataDir = getenv("TEST_DATA_DIR"); 35 | if (!dataDir) { 36 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 37 | goto Lfailure; 38 | } 39 | 40 | char msmiDir[1024]; 41 | int rc = snprintf(msmiDir, sizeof msmiDir, "%s/msmi/OspSystemStructure.xml", dataDir); 42 | if (rc < 0) { 43 | perror(NULL); 44 | goto Lfailure; 45 | } 46 | 47 | execution = cosim_osp_config_execution_create(msmiDir, false, 0); 48 | if (!execution) { goto Lerror; } 49 | 50 | observer = cosim_last_value_observer_create(); 51 | if (!observer) { goto Lerror; } 52 | cosim_execution_add_observer(execution, observer); 53 | 54 | rc = cosim_execution_step(execution, 3); 55 | if (rc < 0) { goto Lerror; } 56 | 57 | size_t numSlaves = cosim_execution_get_num_slaves(execution); 58 | if (numSlaves > MAX_NUMBER_OF_SLAVES) { 59 | printf("Number of slaves in configuration exceeds max number of slaves(%d) supported in test", MAX_NUMBER_OF_SLAVES); 60 | goto Lfailure; 61 | } 62 | 63 | cosim_slave_info infos[MAX_NUMBER_OF_SLAVES]; 64 | rc = cosim_execution_get_slave_infos(execution, &infos[0], numSlaves); 65 | if (rc < 0) { goto Lerror; } 66 | 67 | for (size_t i = 0; i < numSlaves; i++) { 68 | if (0 == strncmp(infos[i].name, "KnuckleBoomCrane", SLAVE_NAME_MAX_SIZE)) { 69 | double value = -1; 70 | cosim_slave_index slaveIndex = infos[i].index; 71 | cosim_value_reference varIndex = 2; 72 | rc = cosim_observer_slave_get_real(observer, slaveIndex, &varIndex, 1, &value); 73 | if (rc < 0) { 74 | goto Lerror; 75 | } 76 | if (value != 0.05) { 77 | fprintf(stderr, "Expected value 0.05, got %f\n", value); 78 | goto Lfailure; 79 | } 80 | } 81 | } 82 | 83 | cosim_execution_start(execution); 84 | Sleep(200); 85 | cosim_execution_stop(execution); 86 | 87 | 88 | goto Lcleanup; 89 | 90 | Lerror: 91 | print_last_error(); 92 | 93 | Lfailure: 94 | exitCode = 1; 95 | 96 | Lcleanup: 97 | cosim_observer_destroy(observer); 98 | cosim_execution_destroy(execution); 99 | return exitCode; 100 | } 101 | -------------------------------------------------------------------------------- /tests/execution_from_ssp_custom_algo_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | void print_last_error() 16 | { 17 | fprintf( 18 | stderr, 19 | "Error code %d: %s\n", 20 | cosim_last_error_code(), cosim_last_error_message()); 21 | } 22 | 23 | int main() 24 | { 25 | cosim_log_setup_simple_console_logging(); 26 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 27 | 28 | int exitCode = 0; 29 | cosim_execution* execution = NULL; 30 | cosim_observer* observer = NULL; 31 | 32 | const char* dataDir = getenv("TEST_DATA_DIR"); 33 | if (!dataDir) { 34 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 35 | goto Lfailure; 36 | } 37 | 38 | char sspDir[1024]; 39 | int rc = snprintf(sspDir, sizeof sspDir, "%s/ssp/demo/no_algorithm_element", dataDir); 40 | if (rc < 0) { 41 | perror(NULL); 42 | goto Lfailure; 43 | } 44 | 45 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 46 | execution = cosim_ssp_fixed_step_execution_create(sspDir, true, 0, nanoStepSize); // override ssp startTime 47 | if (!execution) { goto Lerror; } 48 | 49 | cosim_execution_status status; 50 | cosim_execution_get_status(execution, &status); 51 | 52 | if (status.current_time != 0.0) { 53 | fprintf(stderr, "Expected value 0.0, got %f\n", (double)(status.current_time / 1.0e9)); 54 | goto Lfailure; 55 | } 56 | 57 | observer = cosim_last_value_observer_create(); 58 | if (!observer) { goto Lerror; } 59 | cosim_execution_add_observer(execution, observer); 60 | 61 | rc = cosim_execution_step(execution, 3); 62 | if (rc < 0) { goto Lerror; } 63 | 64 | size_t numSlaves = cosim_execution_get_num_slaves(execution); 65 | 66 | if (numSlaves != 2) { 67 | fprintf(stderr, "Expected numSlaves = 2; got %zu\n", numSlaves); 68 | goto Lfailure; 69 | } 70 | 71 | cosim_slave_info infos[2]; 72 | rc = cosim_execution_get_slave_infos(execution, &infos[0], numSlaves); 73 | if (rc < 0) { goto Lerror; } 74 | 75 | char name[SLAVE_NAME_MAX_SIZE]; 76 | int found_slave = 0; 77 | for (size_t i = 0; i < numSlaves; i++) { 78 | strcpy(name, infos[i].name); 79 | if (0 == strncmp(name, "KnuckleBoomCrane", SLAVE_NAME_MAX_SIZE)) { 80 | found_slave = 1; 81 | double value = -1; 82 | cosim_slave_index slaveIndex = infos[i].index; 83 | cosim_value_reference varIndex = 2; 84 | rc = cosim_observer_slave_get_real(observer, slaveIndex, &varIndex, 1, &value); 85 | if (rc < 0) { 86 | goto Lerror; 87 | } 88 | if (value != 0.05) { 89 | fprintf(stderr, "Expected value 0.05, got %f\n", value); 90 | goto Lfailure; 91 | } 92 | } 93 | } 94 | if (!found_slave) { 95 | fprintf(stderr, "Slave not found: %s\n", name); 96 | goto Lfailure; 97 | } 98 | 99 | cosim_execution_start(execution); 100 | Sleep(100); 101 | cosim_execution_stop(execution); 102 | 103 | goto Lcleanup; 104 | 105 | Lerror: 106 | print_last_error(); 107 | 108 | Lfailure: 109 | exitCode = 1; 110 | 111 | Lcleanup: 112 | cosim_observer_destroy(observer); 113 | cosim_execution_destroy(execution); 114 | return exitCode; 115 | } 116 | -------------------------------------------------------------------------------- /tests/execution_from_ssp_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | void print_last_error() 16 | { 17 | fprintf( 18 | stderr, 19 | "Error code %d: %s\n", 20 | cosim_last_error_code(), cosim_last_error_message()); 21 | } 22 | 23 | int main() 24 | { 25 | cosim_log_setup_simple_console_logging(); 26 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 27 | 28 | int exitCode = 0; 29 | cosim_execution* execution = NULL; 30 | cosim_observer* observer = NULL; 31 | 32 | const char* dataDir = getenv("TEST_DATA_DIR"); 33 | if (!dataDir) { 34 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 35 | goto Lfailure; 36 | } 37 | 38 | char sspDir[1024]; 39 | int rc = snprintf(sspDir, sizeof sspDir, "%s/ssp/demo", dataDir); 40 | if (rc < 0) { 41 | perror(NULL); 42 | goto Lfailure; 43 | } 44 | 45 | execution = cosim_ssp_execution_create(sspDir, false, 0); 46 | if (!execution) { goto Lerror; } 47 | 48 | observer = cosim_last_value_observer_create(); 49 | if (!observer) { goto Lerror; } 50 | cosim_execution_add_observer(execution, observer); 51 | 52 | rc = cosim_execution_step(execution, 3); 53 | if (rc < 0) { goto Lerror; } 54 | 55 | size_t numSlaves = cosim_execution_get_num_slaves(execution); 56 | 57 | if (numSlaves != 2) { 58 | fprintf(stderr, "Expected numSlaves = 2; got %zu\n", numSlaves); 59 | goto Lfailure; 60 | } 61 | 62 | cosim_slave_info infos[2]; 63 | rc = cosim_execution_get_slave_infos(execution, &infos[0], numSlaves); 64 | if (rc < 0) { goto Lerror; } 65 | 66 | char name[SLAVE_NAME_MAX_SIZE]; 67 | int found_slave = 0; 68 | for (size_t i = 0; i < numSlaves; i++) { 69 | strcpy(name, infos[i].name); 70 | if (0 == strncmp(name, "KnuckleBoomCrane", SLAVE_NAME_MAX_SIZE)) { 71 | found_slave = 1; 72 | double value = -1; 73 | cosim_slave_index slaveIndex = infos[i].index; 74 | cosim_value_reference varIndex = 2; 75 | rc = cosim_observer_slave_get_real(observer, slaveIndex, &varIndex, 1, &value); 76 | if (rc < 0) { 77 | goto Lerror; 78 | } 79 | if (value != 0.05) { 80 | fprintf(stderr, "Expected value 0.05, got %f\n", value); 81 | goto Lfailure; 82 | } 83 | } 84 | } 85 | if (!found_slave) { 86 | fprintf(stderr, "Slave not found: %s\n", name); 87 | goto Lfailure; 88 | } 89 | 90 | cosim_execution_start(execution); 91 | Sleep(100); 92 | cosim_execution_stop(execution); 93 | 94 | goto Lcleanup; 95 | 96 | Lerror: 97 | print_last_error(); 98 | 99 | Lfailure: 100 | exitCode = 1; 101 | 102 | Lcleanup: 103 | cosim_observer_destroy(observer); 104 | cosim_execution_destroy(execution); 105 | return exitCode; 106 | } 107 | -------------------------------------------------------------------------------- /tests/inital_values_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | void print_last_error() 10 | { 11 | fprintf( 12 | stderr, 13 | "Error code %d: %s\n", 14 | cosim_last_error_code(), cosim_last_error_message()); 15 | } 16 | 17 | int main() 18 | { 19 | cosim_log_setup_simple_console_logging(); 20 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 21 | 22 | int exitCode = 0; 23 | 24 | cosim_execution* execution = NULL; 25 | cosim_slave* slave = NULL; 26 | cosim_observer* observer = NULL; 27 | 28 | const char* dataDir = getenv("TEST_DATA_DIR"); 29 | if (!dataDir) { 30 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 31 | goto Lfailure; 32 | } 33 | 34 | char fmuPath[1024]; 35 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 36 | if (rc < 0) { 37 | perror(NULL); 38 | goto Lfailure; 39 | } 40 | 41 | // ===== Can step n times and get status 42 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 43 | execution = cosim_execution_create(0, nanoStepSize); 44 | if (!execution) { goto Lerror; } 45 | 46 | slave = cosim_local_slave_create(fmuPath, "slave"); 47 | if (!slave) { goto Lerror; } 48 | 49 | observer = cosim_last_value_observer_create(); 50 | if (!observer) { goto Lerror; } 51 | 52 | cosim_slave_index slave_index = cosim_execution_add_slave(execution, slave); 53 | if (slave_index < 0) { goto Lerror; } 54 | 55 | rc = cosim_execution_add_observer(execution, observer); 56 | if (rc < 0) { goto Lerror; } 57 | 58 | cosim_value_reference realVr = 0; 59 | double initialRealVal = 1.2; 60 | cosim_execution_set_real_initial_value(execution, slave_index, realVr, initialRealVal); 61 | 62 | cosim_value_reference intVr = 0; 63 | int initialIntVal = -5; 64 | cosim_execution_set_integer_initial_value(execution, slave_index, intVr, initialIntVal); 65 | 66 | cosim_value_reference boolVr = 0; 67 | int initialBoolVal = true; 68 | cosim_execution_set_boolean_initial_value(execution, slave_index, boolVr, initialBoolVal); 69 | 70 | cosim_value_reference strVr = 0; 71 | char* initialStrVal = "Hello World!"; 72 | cosim_execution_set_boolean_initial_value(execution, slave_index, strVr, initialStrVal); 73 | 74 | rc = cosim_execution_step(execution, 1); 75 | if (rc < 0) { goto Lerror; } 76 | 77 | double actualRealVal = -1; 78 | rc = cosim_observer_slave_get_real(observer, slave_index, &realVr, 1, &actualRealVal); 79 | if (rc < 0) { goto Lerror; } 80 | 81 | if (actualRealVal != initialRealVal) { 82 | fprintf(stderr, "Expected value %f, got %f\n", initialRealVal, actualRealVal); 83 | goto Lfailure; 84 | } 85 | 86 | int actualIntVal = -1; 87 | rc = cosim_observer_slave_get_integer(observer, slave_index, &intVr, 1, &actualIntVal); 88 | if (rc < 0) { goto Lerror; } 89 | 90 | if (actualIntVal != initialIntVal) { 91 | fprintf(stderr, "Expected value %i, got %i\n", initialIntVal, actualIntVal); 92 | goto Lfailure; 93 | } 94 | 95 | bool actualBoolVal = 0; 96 | rc = cosim_observer_slave_get_boolean(observer, slave_index, &boolVr, 1, &actualBoolVal); 97 | if (rc < 0) { goto Lerror; } 98 | 99 | if (actualBoolVal != initialBoolVal) { 100 | fprintf(stderr, "Expected value %i, got %i\n", initialBoolVal, actualBoolVal); 101 | goto Lfailure; 102 | } 103 | 104 | const char* actualStrVal = NULL; 105 | rc = cosim_observer_slave_get_string(observer, slave_index, &boolVr, 1, &actualStrVal); 106 | if (rc < 0) { goto Lerror; } 107 | 108 | if (!strcmp(actualStrVal, initialStrVal)) { 109 | fprintf(stderr, "Expected value %s, got %s\n", initialStrVal, actualStrVal); 110 | goto Lfailure; 111 | } 112 | 113 | goto Lcleanup; 114 | 115 | Lerror: 116 | print_last_error(); 117 | 118 | Lfailure: 119 | exitCode = 1; 120 | 121 | Lcleanup: 122 | cosim_observer_destroy(observer); 123 | cosim_local_slave_destroy(slave); 124 | cosim_execution_destroy(execution); 125 | 126 | return exitCode; 127 | } 128 | -------------------------------------------------------------------------------- /tests/load_config_and_teardown_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | void print_last_error() 16 | { 17 | fprintf( 18 | stderr, 19 | "Error code %d: %s\n", 20 | cosim_last_error_code(), cosim_last_error_message()); 21 | } 22 | 23 | int main() 24 | { 25 | cosim_log_setup_simple_console_logging(); 26 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 27 | 28 | const char* dataDir = getenv("TEST_DATA_DIR"); 29 | if (!dataDir) { 30 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 31 | return 1; 32 | } 33 | 34 | char sspDir[1024]; 35 | int rc = snprintf(sspDir, sizeof sspDir, "%s/ssp/demo", dataDir); 36 | if (rc < 0) { 37 | perror(NULL); 38 | return 1; 39 | } 40 | 41 | cosim_execution* execution = cosim_ssp_execution_create(sspDir, false, 0); 42 | if (!execution) { 43 | print_last_error(); 44 | return 1; 45 | } 46 | 47 | cosim_execution_destroy(execution); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /tests/multiple_fmus_execution_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | void print_last_error() 16 | { 17 | fprintf( 18 | stderr, 19 | "Error code %d: %s\n", 20 | cosim_last_error_code(), cosim_last_error_message()); 21 | } 22 | 23 | int main() 24 | { 25 | cosim_log_setup_simple_console_logging(); 26 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 27 | 28 | int exitCode = 0; 29 | 30 | cosim_execution* execution = NULL; 31 | cosim_slave* slave1 = NULL; 32 | cosim_slave* slave2 = NULL; 33 | cosim_observer* observer1 = NULL; 34 | cosim_observer* observer2 = NULL; 35 | cosim_manipulator* manipulator = NULL; 36 | 37 | const char* dataDir = getenv("TEST_DATA_DIR"); 38 | if (!dataDir) { 39 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 40 | goto Lfailure; 41 | } 42 | 43 | char fmuPath[1024]; 44 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 45 | if (rc < 0) { 46 | perror(NULL); 47 | goto Lfailure; 48 | } 49 | 50 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 51 | execution = cosim_execution_create(0, nanoStepSize); 52 | if (!execution) { goto Lerror; } 53 | 54 | slave1 = cosim_local_slave_create(fmuPath, "slave1"); 55 | if (!slave1) { goto Lerror; } 56 | 57 | slave2 = cosim_local_slave_create(fmuPath, "slave2"); 58 | if (!slave2) { goto Lerror; } 59 | 60 | observer1 = cosim_last_value_observer_create(); 61 | if (!observer1) { goto Lerror; } 62 | 63 | observer2 = cosim_last_value_observer_create(); 64 | if (!observer2) { goto Lerror; } 65 | 66 | manipulator = cosim_override_manipulator_create(); 67 | if (!manipulator) { goto Lerror; } 68 | 69 | rc = cosim_execution_add_manipulator(execution, manipulator); 70 | if (rc < 0) { goto Lerror; } 71 | 72 | cosim_slave_index slave_index1 = cosim_execution_add_slave(execution, slave1); 73 | if (slave_index1 < 0) { goto Lerror; } 74 | cosim_slave_index slave_index2 = cosim_execution_add_slave(execution, slave2); 75 | if (slave_index2 < 0) { goto Lerror; } 76 | 77 | rc = cosim_execution_add_observer(execution, observer1); 78 | if (rc < 0) { goto Lerror; } 79 | 80 | rc = cosim_execution_add_observer(execution, observer2); 81 | if (rc < 0) { goto Lerror; } 82 | 83 | 84 | cosim_value_reference realInVar = 0; 85 | const double realInVal = 5.0; 86 | rc = cosim_manipulator_slave_set_real(manipulator, slave_index1, &realInVar, 1, &realInVal); 87 | if (rc < 0) { goto Lerror; } 88 | 89 | cosim_value_reference intInVar = 0; 90 | const int intInVal = 42; 91 | rc = cosim_manipulator_slave_set_integer(manipulator, slave_index1, &intInVar, 1, &intInVal); 92 | if (rc < 0) { goto Lerror; } 93 | 94 | cosim_value_reference boolInVar = 0; 95 | const bool boolInVal = true; 96 | rc = cosim_manipulator_slave_set_boolean(manipulator, slave_index1, &boolInVar, 1, &boolInVal); 97 | if (rc < 0) { goto Lerror; } 98 | 99 | cosim_value_reference strInVar = 0; 100 | const char* strInVal = "foo"; 101 | rc = cosim_manipulator_slave_set_string(manipulator, slave_index1, &strInVar, 1, &strInVal); 102 | if (rc < 0) { goto Lerror; } 103 | 104 | rc = cosim_execution_step(execution, 10); 105 | if (rc < 0) { goto Lerror; } 106 | 107 | cosim_execution_status executionStatus; 108 | rc = cosim_execution_get_status(execution, &executionStatus); 109 | if (rc < 0) { goto Lerror; } 110 | 111 | double precision = 1e-9; 112 | double simTime = executionStatus.current_time * 1e-9; 113 | if (fabs(simTime - 1.0) > precision) { 114 | fprintf(stderr, "Expected current time == 1.0 s, got %f\n", simTime); 115 | goto Lfailure; 116 | } 117 | 118 | if (executionStatus.state != COSIM_EXECUTION_STOPPED) { 119 | fprintf(stderr, "Expected state == %i, got %i\n", COSIM_EXECUTION_STOPPED, executionStatus.state); 120 | goto Lfailure; 121 | } 122 | 123 | if (executionStatus.error_code != COSIM_ERRC_SUCCESS) { 124 | fprintf(stderr, "Expected error code == %i, got %i\n", COSIM_ERRC_SUCCESS, executionStatus.error_code); 125 | goto Lfailure; 126 | } 127 | 128 | cosim_value_reference realOutVar = 0; 129 | double realOutVal = -1.0; 130 | rc = cosim_observer_slave_get_real(observer1, slave_index1, &realOutVar, 1, &realOutVal); 131 | if (rc < 0) { goto Lerror; } 132 | 133 | if (realOutVal != 5.0) { 134 | fprintf(stderr, "Expected value 5.0, got %f\n", realOutVal); 135 | goto Lfailure; 136 | } 137 | 138 | cosim_value_reference intOutVar = 0; 139 | int intOutVal = 10; 140 | rc = cosim_observer_slave_get_integer(observer1, slave_index1, &intOutVar, 1, &intOutVal); 141 | if (rc < 0) { goto Lerror; } 142 | 143 | if (intOutVal != 42) { 144 | fprintf(stderr, "Expected value 42, got %i\n", intOutVal); 145 | goto Lfailure; 146 | } 147 | 148 | cosim_value_reference boolOutVar = 0; 149 | bool boolOutVal = false; 150 | rc = cosim_observer_slave_get_boolean(observer1, slave_index1, &boolOutVar, 1, &boolOutVal); 151 | if (rc < 0) { goto Lerror; } 152 | 153 | if (boolOutVal != true) { 154 | fprintf(stderr, "Expected value true, got %s\n", boolOutVal > 0 ? "true" : "false"); 155 | goto Lfailure; 156 | } 157 | 158 | cosim_value_reference strOutVar = 0; 159 | const char* strOutVal; 160 | rc = cosim_observer_slave_get_string(observer1, slave_index1, &strOutVar, 1, &strOutVal); 161 | if (rc < 0) { goto Lerror; } 162 | 163 | if (0 != strncmp(strOutVal, "foo", SLAVE_NAME_MAX_SIZE)) { 164 | fprintf(stderr, "Expected value foo, got %s\n", strOutVal); 165 | goto Lfailure; 166 | } 167 | 168 | rc = cosim_observer_slave_get_real(observer2, slave_index2, &realOutVar, 1, &realOutVal); 169 | if (rc < 0) { goto Lerror; } 170 | 171 | rc = cosim_observer_slave_get_integer(observer2, slave_index2, &intOutVar, 1, &intOutVal); 172 | if (rc < 0) { goto Lerror; } 173 | 174 | if (realOutVal != 0.0) { 175 | fprintf(stderr, "Expected value 0.0, got %f\n", realOutVal); 176 | goto Lfailure; 177 | } 178 | if (intOutVal != 0) { 179 | fprintf(stderr, "Expected value 0, got %i\n", intOutVal); 180 | goto Lfailure; 181 | } 182 | 183 | goto Lcleanup; 184 | 185 | Lerror: 186 | print_last_error(); 187 | 188 | Lfailure: 189 | exitCode = 1; 190 | 191 | Lcleanup: 192 | cosim_manipulator_destroy(manipulator); 193 | cosim_observer_destroy(observer1); 194 | cosim_observer_destroy(observer2); 195 | cosim_local_slave_destroy(slave2); 196 | cosim_local_slave_destroy(slave1); 197 | cosim_execution_destroy(execution); 198 | 199 | return exitCode; 200 | } 201 | -------------------------------------------------------------------------------- /tests/observer_can_buffer_samples.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | void print_last_error() 16 | { 17 | fprintf( 18 | stderr, 19 | "Error code %d: %s\n", 20 | cosim_last_error_code(), cosim_last_error_message()); 21 | } 22 | 23 | int main() 24 | { 25 | cosim_log_setup_simple_console_logging(); 26 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 27 | 28 | int exitCode = 0; 29 | 30 | cosim_execution* execution = NULL; 31 | cosim_slave* slave = NULL; 32 | cosim_observer* observer = NULL; 33 | cosim_manipulator* manipulator = NULL; 34 | 35 | const char* dataDir = getenv("TEST_DATA_DIR"); 36 | if (!dataDir) { 37 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 38 | goto Lfailure; 39 | } 40 | 41 | char fmuPath[1024]; 42 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 43 | if (rc < 0) { 44 | perror(NULL); 45 | goto Lfailure; 46 | } 47 | 48 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 49 | execution = cosim_execution_create(0, nanoStepSize); 50 | if (!execution) { goto Lerror; } 51 | 52 | slave = cosim_local_slave_create(fmuPath, "slave"); 53 | if (!slave) { goto Lerror; } 54 | 55 | observer = cosim_time_series_observer_create(); 56 | if (!observer) { goto Lerror; } 57 | 58 | cosim_slave_index slaveIndex = cosim_execution_add_slave(execution, slave); 59 | if (slaveIndex < 0) { goto Lerror; } 60 | 61 | rc = cosim_execution_add_observer(execution, observer); 62 | if (rc < 0) { goto Lerror; } 63 | 64 | manipulator = cosim_override_manipulator_create(); 65 | if (!manipulator) { goto Lerror; } 66 | 67 | rc = cosim_execution_add_manipulator(execution, manipulator); 68 | if (rc < 0) { goto Lerror; } 69 | 70 | double inputRealSamples[10] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; 71 | int inputIntSamples[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 72 | 73 | cosim_value_reference reference = 0; 74 | 75 | rc = cosim_observer_start_observing(observer, slaveIndex, COSIM_VARIABLE_TYPE_REAL, reference); 76 | if (rc < 0) { goto Lerror; } 77 | rc = cosim_observer_start_observing(observer, slaveIndex, COSIM_VARIABLE_TYPE_INTEGER, reference); 78 | if (rc < 0) { goto Lerror; } 79 | 80 | for (int i = 0; i < 10; i++) { 81 | rc = cosim_manipulator_slave_set_real(manipulator, 0, &reference, 1, &inputRealSamples[i]); 82 | if (rc < 0) { goto Lerror; } 83 | rc = cosim_manipulator_slave_set_integer(manipulator, 0, &reference, 1, &inputIntSamples[i]); 84 | if (rc < 0) { goto Lerror; } 85 | rc = cosim_execution_step(execution, 1); 86 | if (rc < 0) { goto Lerror; } 87 | } 88 | 89 | cosim_step_number fromStep = 1; 90 | const size_t nSamples = 10; 91 | double realSamples[10]; 92 | int intSamples[10]; 93 | cosim_time_point times[10]; 94 | cosim_step_number steps[10]; 95 | 96 | int64_t readRealSamples = cosim_observer_slave_get_real_samples(observer, slaveIndex, reference, fromStep, nSamples, realSamples, steps, times); 97 | if (readRealSamples != (int64_t)nSamples) { 98 | print_last_error(); 99 | fprintf(stderr, "Expected to read 10 real samples, got %" PRId64 "\n", readRealSamples); 100 | goto Lfailure; 101 | } 102 | 103 | int64_t readIntSamples = cosim_observer_slave_get_integer_samples(observer, slaveIndex, reference, fromStep, nSamples, intSamples, steps, times); 104 | if (readIntSamples != (int64_t)nSamples) { 105 | print_last_error(); 106 | fprintf(stderr, "Expected to read 10 int samples, got %" PRId64 "\n", readIntSamples); 107 | goto Lfailure; 108 | } 109 | 110 | long expectedSteps[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 111 | double expectedRealSamples[10] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; 112 | int expectedIntSamples[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 113 | double t[10] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; 114 | cosim_time_point expectedTimeSamples[10]; 115 | for (int j = 0; j < 10; ++j) { 116 | expectedTimeSamples[j] = (cosim_time_point)(1.0e9 * t[j]); 117 | } 118 | 119 | for (int i = 0; i < 10; i++) { 120 | if (fabs(expectedRealSamples[i] - realSamples[i]) > 0.000001) { 121 | fprintf(stderr, "Sample nr %d expected real sample %lf, got %lf\n", i, expectedRealSamples[i], realSamples[i]); 122 | goto Lfailure; 123 | } 124 | if (expectedIntSamples[i] != intSamples[i]) { 125 | fprintf(stderr, "Sample nr %d expected int sample %d, got %d\n", i, expectedIntSamples[i], intSamples[i]); 126 | goto Lfailure; 127 | } 128 | if (expectedSteps[i] != steps[i]) { 129 | fprintf(stderr, "Sample nr %d expected step %li, got %" PRId64 "\n", i, expectedSteps[i], steps[i]); 130 | goto Lfailure; 131 | } 132 | if (expectedTimeSamples[i] != times[i]) { 133 | fprintf(stderr, "Sample nr %d expected time sample %" PRId64 ", got %" PRId64 "\n", i, expectedTimeSamples[i], times[i]); 134 | goto Lfailure; 135 | } 136 | } 137 | 138 | cosim_step_number nums[2]; 139 | cosim_duration dur = (cosim_time_point)(0.5 * 1.0e9); 140 | rc = cosim_observer_get_step_numbers_for_duration(observer, 0, dur, nums); 141 | if (rc < 0) { goto Lerror; } 142 | if (nums[0] != 5) { 143 | fprintf(stderr, "Expected step number %i, got %" PRId64 "\n", 5, nums[0]); 144 | goto Lfailure; 145 | } 146 | if (nums[1] != 10) { 147 | fprintf(stderr, "Expected step number %i, got %" PRId64 "\n", 10, nums[1]); 148 | goto Lfailure; 149 | } 150 | 151 | cosim_time_point t1 = (cosim_time_point)(0.3 * 1e9); 152 | cosim_time_point t2 = (cosim_time_point)(0.6 * 1e9); 153 | rc = cosim_observer_get_step_numbers(observer, 0, t1, t2, nums); 154 | if (rc < 0) { goto Lerror; } 155 | if (nums[0] != 3) { 156 | fprintf(stderr, "Expected step number %i, got %" PRId64 "\n", 3, nums[0]); 157 | goto Lfailure; 158 | } 159 | if (nums[1] != 6) { 160 | fprintf(stderr, "Expected step number %i, got %" PRId64 "\n", 6, nums[1]); 161 | goto Lfailure; 162 | } 163 | 164 | goto Lcleanup; 165 | 166 | Lerror: 167 | print_last_error(); 168 | 169 | Lfailure: 170 | exitCode = 1; 171 | 172 | Lcleanup: 173 | cosim_manipulator_destroy(manipulator); 174 | cosim_observer_destroy(observer); 175 | cosim_local_slave_destroy(slave); 176 | cosim_execution_destroy(execution); 177 | 178 | return exitCode; 179 | } 180 | -------------------------------------------------------------------------------- /tests/observer_initial_samples_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef _WINDOWS 8 | # include 9 | #else 10 | # include 11 | # define Sleep(x) usleep((x)*1000) 12 | #endif 13 | 14 | void print_last_error() 15 | { 16 | fprintf( 17 | stderr, 18 | "Error code %d: %s\n", 19 | cosim_last_error_code(), cosim_last_error_message()); 20 | } 21 | 22 | int main() 23 | { 24 | cosim_log_setup_simple_console_logging(); 25 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 26 | 27 | int exitCode = 0; 28 | 29 | cosim_execution* execution = NULL; 30 | cosim_slave* slave = NULL; 31 | cosim_observer* observer = NULL; 32 | cosim_manipulator* manipulator = NULL; 33 | 34 | const char* dataDir = getenv("TEST_DATA_DIR"); 35 | if (!dataDir) { 36 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 37 | goto Lfailure; 38 | } 39 | 40 | char fmuPath[1024]; 41 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 42 | if (rc < 0) { 43 | perror(NULL); 44 | goto Lfailure; 45 | } 46 | 47 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 48 | execution = cosim_execution_create(0, nanoStepSize); 49 | if (!execution) { goto Lerror; } 50 | 51 | slave = cosim_local_slave_create(fmuPath, "slave"); 52 | if (!slave) { goto Lerror; } 53 | 54 | observer = cosim_last_value_observer_create(); 55 | if (!observer) { goto Lerror; } 56 | 57 | cosim_slave_index slave_index = cosim_execution_add_slave(execution, slave); 58 | if (slave_index < 0) { goto Lerror; } 59 | 60 | rc = cosim_execution_add_observer(execution, observer); 61 | if (rc < 0) { goto Lerror; } 62 | 63 | manipulator = cosim_override_manipulator_create(); 64 | if (!manipulator) { goto Lerror; } 65 | 66 | rc = cosim_execution_add_manipulator(execution, manipulator); 67 | if (rc < 0) { goto Lerror; } 68 | 69 | // ===== Getting real before step 70 | 71 | cosim_value_reference realOutVar = 0; 72 | double realOutVal = -1.0; 73 | rc = cosim_observer_slave_get_real(observer, slave_index, &realOutVar, 1, &realOutVal); 74 | if (rc < 0) { goto Lerror; } 75 | 76 | double realVal = 1.2; 77 | rc = cosim_manipulator_slave_set_real(manipulator, 0, &realOutVar, 1, &realVal); 78 | if (rc < 0) { goto Lerror; } 79 | 80 | rc = cosim_execution_step(execution, 10); 81 | if (rc < 0) { goto Lerror; } 82 | 83 | if (realOutVal != 0.0) { 84 | fprintf(stderr, "Expected value 0.0, got %f\n", realOutVal); 85 | goto Lfailure; 86 | } 87 | 88 | goto Lcleanup; 89 | 90 | Lerror: 91 | print_last_error(); 92 | 93 | Lfailure: 94 | exitCode = 1; 95 | 96 | Lcleanup: 97 | cosim_manipulator_destroy(manipulator); 98 | cosim_observer_destroy(observer); 99 | cosim_local_slave_destroy(slave); 100 | cosim_execution_destroy(execution); 101 | 102 | return exitCode; 103 | } 104 | -------------------------------------------------------------------------------- /tests/observer_multiple_slaves_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef _WINDOWS 8 | # include 9 | #else 10 | # include 11 | # define Sleep(x) usleep((x)*1000) 12 | #endif 13 | 14 | void print_last_error() 15 | { 16 | fprintf( 17 | stderr, 18 | "Error code %d: %s\n", 19 | cosim_last_error_code(), cosim_last_error_message()); 20 | } 21 | 22 | int main() 23 | { 24 | cosim_log_setup_simple_console_logging(); 25 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 26 | 27 | int exitCode = 0; 28 | 29 | cosim_execution* execution = NULL; 30 | cosim_slave* slave1 = NULL; 31 | cosim_slave* slave2 = NULL; 32 | cosim_observer* observer = NULL; 33 | cosim_manipulator* manipulator = NULL; 34 | 35 | const char* dataDir = getenv("TEST_DATA_DIR"); 36 | if (!dataDir) { 37 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 38 | return 1; 39 | } 40 | 41 | char fmuPath[1024]; 42 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 43 | if (rc < 0) { 44 | perror(NULL); 45 | return 1; 46 | } 47 | 48 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 49 | execution = cosim_execution_create(0, nanoStepSize); 50 | if (!execution) { goto Lerror; } 51 | 52 | slave1 = cosim_local_slave_create(fmuPath, "slave1"); 53 | if (!slave1) { goto Lerror; } 54 | slave2 = cosim_local_slave_create(fmuPath, "slave2"); 55 | if (!slave2) { goto Lerror; } 56 | 57 | observer = cosim_last_value_observer_create(); 58 | if (!observer) { goto Lerror; } 59 | 60 | cosim_slave_index slaveIndex1 = cosim_execution_add_slave(execution, slave1); 61 | if (slaveIndex1 < 0) { goto Lerror; } 62 | cosim_slave_index slaveIndex2 = cosim_execution_add_slave(execution, slave2); 63 | if (slaveIndex2 < 0) { goto Lerror; } 64 | 65 | rc = cosim_execution_add_observer(execution, observer); 66 | if (rc < 0) { goto Lerror; } 67 | 68 | manipulator = cosim_override_manipulator_create(); 69 | if (!manipulator) { goto Lerror; } 70 | 71 | rc = cosim_execution_add_manipulator(execution, manipulator); 72 | if (rc < 0) { goto Lerror; } 73 | 74 | cosim_value_reference realInVar = 0; 75 | const double realInVal = 5.0; 76 | rc = cosim_manipulator_slave_set_real(manipulator, slaveIndex1, &realInVar, 1, &realInVal); 77 | if (rc < 0) { goto Lerror; } 78 | 79 | cosim_value_reference intInVar = 0; 80 | const int intInVal = 42; 81 | rc = cosim_manipulator_slave_set_integer(manipulator, slaveIndex1, &intInVar, 1, &intInVal); 82 | if (rc < 0) { goto Lerror; } 83 | 84 | rc = cosim_execution_step(execution, 10); 85 | if (rc < 0) { goto Lerror; } 86 | 87 | cosim_execution_status executionStatus; 88 | rc = cosim_execution_get_status(execution, &executionStatus); 89 | if (rc < 0) { goto Lerror; } 90 | 91 | double precision = 1e-9; 92 | double simTime = executionStatus.current_time * 1e-9; 93 | if (fabs(simTime - 1.0) > precision) { 94 | fprintf(stderr, "Expected current time == 1.0s, got %f\n", simTime); 95 | goto Lfailure; 96 | } 97 | 98 | if (executionStatus.state != COSIM_EXECUTION_STOPPED) { 99 | fprintf(stderr, "Expected state == %i, got %i\n", COSIM_EXECUTION_STOPPED, executionStatus.state); 100 | goto Lfailure; 101 | } 102 | 103 | if (executionStatus.error_code != COSIM_ERRC_SUCCESS) { 104 | fprintf(stderr, "Expected error code == %i, got %i\n", COSIM_ERRC_SUCCESS, executionStatus.error_code); 105 | goto Lfailure; 106 | } 107 | 108 | cosim_value_reference realOutVar = 0; 109 | double realOutVal = -1.0; 110 | rc = cosim_observer_slave_get_real(observer, slaveIndex1, &realOutVar, 1, &realOutVal); 111 | if (rc < 0) { goto Lerror; } 112 | 113 | cosim_value_reference intOutVar = 0; 114 | int intOutVal = 10; 115 | rc = cosim_observer_slave_get_integer(observer, slaveIndex1, &intOutVar, 1, &intOutVal); 116 | if (rc < 0) { goto Lerror; } 117 | 118 | if (realOutVal != 5.0) { 119 | fprintf(stderr, "Expected value 5.0, got %f\n", realOutVal); 120 | goto Lfailure; 121 | } 122 | if (intOutVal != 42) { 123 | fprintf(stderr, "Expected value 42, got %i\n", intOutVal); 124 | goto Lfailure; 125 | } 126 | 127 | rc = cosim_observer_slave_get_real(observer, slaveIndex2, &realOutVar, 1, &realOutVal); 128 | if (rc < 0) { goto Lerror; } 129 | 130 | rc = cosim_observer_slave_get_integer(observer, slaveIndex2, &intOutVar, 1, &intOutVal); 131 | if (rc < 0) { goto Lerror; } 132 | 133 | if (realOutVal != 0.0) { 134 | fprintf(stderr, "Expected value 0.0, got %f\n", realOutVal); 135 | goto Lfailure; 136 | } 137 | if (intOutVal != 0) { 138 | fprintf(stderr, "Expected value 0, got %i\n", intOutVal); 139 | goto Lfailure; 140 | } 141 | 142 | goto Lcleanup; 143 | 144 | Lerror: 145 | print_last_error(); 146 | 147 | Lfailure: 148 | exitCode = 1; 149 | 150 | Lcleanup: 151 | cosim_manipulator_destroy(manipulator); 152 | cosim_observer_destroy(observer); 153 | cosim_local_slave_destroy(slave2); 154 | cosim_local_slave_destroy(slave1); 155 | cosim_execution_destroy(execution); 156 | 157 | return exitCode; 158 | } 159 | -------------------------------------------------------------------------------- /tests/simulation_error_handling_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | void print_last_error() 16 | { 17 | fprintf( 18 | stderr, 19 | "Error code %d: %s\n", 20 | cosim_last_error_code(), cosim_last_error_message()); 21 | } 22 | 23 | int main() 24 | { 25 | cosim_log_setup_simple_console_logging(); 26 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 27 | 28 | int exitCode = 0; 29 | cosim_execution* execution = NULL; 30 | cosim_manipulator* manipulator = NULL; 31 | cosim_slave* slave = NULL; 32 | 33 | const char* dataDir = getenv("TEST_DATA_DIR"); 34 | if (!dataDir) { 35 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 36 | goto Lfailure; 37 | } 38 | 39 | char fmuPath[1024]; 40 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi2/fail.fmu", dataDir); 41 | if (rc < 0) { 42 | perror(NULL); 43 | goto Lfailure; 44 | } 45 | 46 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 47 | execution = cosim_execution_create(0, nanoStepSize); 48 | if (!execution) { goto Lerror; } 49 | 50 | slave = cosim_local_slave_create(fmuPath, "slave"); 51 | if (!slave) { goto Lerror; } 52 | 53 | cosim_slave_index slave_index = cosim_execution_add_slave(execution, slave); 54 | if (slave_index < 0) { goto Lerror; } 55 | 56 | manipulator = cosim_override_manipulator_create(); 57 | if (!manipulator) { goto Lerror; } 58 | 59 | rc = cosim_execution_add_manipulator(execution, manipulator); 60 | if (rc < 0) { goto Lerror; } 61 | 62 | rc = cosim_execution_step(execution, 1); 63 | if (rc < 0) { goto Lerror; } 64 | 65 | cosim_execution_status status; 66 | rc = cosim_execution_get_status(execution, &status); 67 | if (rc < 0) { 68 | fprintf(stderr, "Expected call to cosim_execution_get_status() 1 to return success."); 69 | goto Lfailure; 70 | } 71 | 72 | rc = cosim_execution_start(execution); 73 | if (rc < 0) { goto Lerror; } 74 | 75 | Sleep(100); 76 | 77 | rc = cosim_execution_get_status(execution, &status); 78 | if (rc < 0) { 79 | fprintf(stderr, "Expected call to cosim_execution_get_status() 2 to return success."); 80 | goto Lfailure; 81 | } 82 | 83 | cosim_value_reference ref = 0; 84 | const bool val = true; 85 | // Produces a model error in the subsequent step 86 | rc = cosim_manipulator_slave_set_boolean(manipulator, slave_index, &ref, 1, &val); 87 | if (rc < 0) { goto Lerror; } 88 | 89 | // Need to wait a bit due to stepping (and failure) happening in another thread. 90 | Sleep(400); 91 | 92 | rc = cosim_execution_get_status(execution, &status); 93 | if (rc >= 0) { 94 | fprintf(stderr, "Expected call to cosim_execution_get_status() 3 to return failure."); 95 | goto Lfailure; 96 | } 97 | 98 | rc = cosim_execution_get_status(execution, &status); 99 | if (rc >= 0) { 100 | fprintf(stderr, "Expected call to cosim_execution_get_status() 4 to return failure."); 101 | goto Lfailure; 102 | } 103 | 104 | if (status.state != COSIM_EXECUTION_ERROR) { 105 | fprintf(stderr, "Expected state == %i, got %i\n", COSIM_EXECUTION_ERROR, status.state); 106 | goto Lfailure; 107 | } 108 | 109 | print_last_error(); 110 | const char* lastErrorMessage = cosim_last_error_message(); 111 | if (0 == strncmp(lastErrorMessage, "", 1)) { 112 | fprintf(stdout, "Expected to find an error message, but last error was: %s\n", lastErrorMessage); 113 | goto Lfailure; 114 | } 115 | int lastErrorCode = cosim_last_error_code(); 116 | if (lastErrorCode != COSIM_ERRC_SIMULATION_ERROR) { 117 | fprintf(stdout, "Expected to find error code %i, but got error code: %i\n", COSIM_ERRC_SIMULATION_ERROR, lastErrorCode); 118 | goto Lfailure; 119 | } 120 | 121 | // What do we expect should happen if calling further methods? 122 | Sleep(100); 123 | rc = cosim_execution_stop(execution); 124 | if (rc >= 0) { goto Lfailure; } 125 | 126 | goto Lcleanup; 127 | 128 | Lerror: 129 | print_last_error(); 130 | 131 | Lfailure: 132 | exitCode = 1; 133 | 134 | Lcleanup: 135 | cosim_manipulator_destroy(manipulator); 136 | cosim_local_slave_destroy(slave); 137 | cosim_execution_destroy(execution); 138 | return exitCode; 139 | } 140 | -------------------------------------------------------------------------------- /tests/single_fmu_execution_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef _WINDOWS 8 | # include 9 | #else 10 | # include 11 | # define Sleep(x) usleep((x)*1000) 12 | #endif 13 | 14 | void print_last_error() 15 | { 16 | fprintf( 17 | stderr, 18 | "Error code %d: %s\n", 19 | cosim_last_error_code(), cosim_last_error_message()); 20 | } 21 | 22 | int main() 23 | { 24 | cosim_log_setup_simple_console_logging(); 25 | cosim_log_set_output_level(COSIM_LOG_SEVERITY_INFO); 26 | 27 | int exitCode = 0; 28 | 29 | cosim_execution* execution = NULL; 30 | cosim_slave* slave = NULL; 31 | cosim_observer* observer = NULL; 32 | cosim_manipulator* manipulator = NULL; 33 | 34 | const char* dataDir = getenv("TEST_DATA_DIR"); 35 | if (!dataDir) { 36 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 37 | goto Lfailure; 38 | } 39 | 40 | char fmuPath[1024]; 41 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 42 | if (rc < 0) { 43 | perror(NULL); 44 | goto Lfailure; 45 | } 46 | 47 | // ===== Can step n times and get status 48 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 49 | execution = cosim_execution_create(0, nanoStepSize); 50 | if (!execution) { goto Lerror; } 51 | 52 | slave = cosim_local_slave_create(fmuPath, "slave"); 53 | if (!slave) { goto Lerror; } 54 | 55 | observer = cosim_last_value_observer_create(); 56 | if (!observer) { goto Lerror; } 57 | 58 | cosim_slave_index slave_index = cosim_execution_add_slave(execution, slave); 59 | if (slave_index < 0) { goto Lerror; } 60 | 61 | rc = cosim_execution_add_observer(execution, observer); 62 | if (rc < 0) { goto Lerror; } 63 | 64 | rc = cosim_execution_step(execution, 10); 65 | if (rc < 0) { goto Lerror; } 66 | 67 | cosim_execution_status executionStatus; 68 | rc = cosim_execution_get_status(execution, &executionStatus); 69 | if (rc < 0) { goto Lerror; } 70 | 71 | double precision = 1e-9; 72 | double simTime = executionStatus.current_time * 1e-9; 73 | if (fabs(simTime - 1.0) > precision) { 74 | fprintf(stderr, "Expected current time == 1.0, got %f\n", simTime); 75 | goto Lfailure; 76 | } 77 | 78 | if (executionStatus.state != COSIM_EXECUTION_STOPPED) { 79 | fprintf(stderr, "Expected state == %i, got %i\n", COSIM_EXECUTION_STOPPED, executionStatus.state); 80 | goto Lfailure; 81 | } 82 | 83 | if (executionStatus.error_code != COSIM_ERRC_SUCCESS) { 84 | fprintf(stderr, "Expected error code == %i, got %i\n", COSIM_ERRC_SUCCESS, executionStatus.error_code); 85 | goto Lfailure; 86 | } 87 | 88 | manipulator = cosim_override_manipulator_create(); 89 | if (!manipulator) { goto Lerror; } 90 | 91 | rc = cosim_execution_add_manipulator(execution, manipulator); 92 | if (rc < 0) { goto Lerror; } 93 | 94 | // ===== Can start/stop execution and get status 95 | cosim_value_reference realInVar = 0; 96 | const double realInVal = 5.0; 97 | rc = cosim_manipulator_slave_set_real(manipulator, slave_index, &realInVar, 1, &realInVal); 98 | if (rc < 0) { goto Lerror; } 99 | 100 | cosim_value_reference intInVar = 0; 101 | const int intInVal = 42; 102 | rc = cosim_manipulator_slave_set_integer(manipulator, slave_index, &intInVar, 1, &intInVal); 103 | if (rc < 0) { goto Lerror; } 104 | 105 | rc = cosim_execution_start(execution); 106 | if (rc < 0) { goto Lerror; } 107 | 108 | rc = cosim_execution_get_status(execution, &executionStatus); 109 | if (rc < 0) { goto Lerror; } 110 | 111 | if (executionStatus.state != COSIM_EXECUTION_RUNNING) { 112 | fprintf(stderr, "Expected state == %i, got %i\n", COSIM_EXECUTION_RUNNING, executionStatus.state); 113 | goto Lfailure; 114 | } 115 | 116 | if (executionStatus.error_code != COSIM_ERRC_SUCCESS) { 117 | fprintf(stderr, "Expected error code == %i, got %i\n", COSIM_ERRC_SUCCESS, executionStatus.error_code); 118 | goto Lfailure; 119 | } 120 | 121 | Sleep(100); 122 | 123 | rc = cosim_execution_stop(execution); 124 | if (rc < 0) { goto Lerror; } 125 | 126 | rc = cosim_execution_get_status(execution, &executionStatus); 127 | if (rc < 0) { goto Lerror; } 128 | 129 | if (executionStatus.state != COSIM_EXECUTION_STOPPED) { 130 | fprintf(stderr, "Expected state == %i, got %i\n", COSIM_EXECUTION_STOPPED, executionStatus.state); 131 | goto Lfailure; 132 | } 133 | 134 | if (executionStatus.error_code != COSIM_ERRC_SUCCESS) { 135 | fprintf(stderr, "Expected error code == %i, got %i\n", COSIM_ERRC_SUCCESS, executionStatus.error_code); 136 | goto Lfailure; 137 | } 138 | 139 | cosim_value_reference realOutVar = 0; 140 | double realOutVal = -1.0; 141 | rc = cosim_observer_slave_get_real(observer, slave_index, &realOutVar, 1, &realOutVal); 142 | if (rc < 0) { goto Lerror; } 143 | 144 | 145 | cosim_value_reference intOutVar = 0; 146 | int intOutVal = 10; 147 | rc = cosim_observer_slave_get_integer(observer, slave_index, &intOutVar, 1, &intOutVal); 148 | if (rc < 0) { goto Lerror; } 149 | 150 | if (realOutVal != 5.0) { 151 | fprintf(stderr, "Expected value 5.0, got %f\n", realOutVal); 152 | goto Lfailure; 153 | } 154 | if (intOutVal != 42) { 155 | fprintf(stderr, "Expected value 42, got %i\n", intOutVal); 156 | goto Lfailure; 157 | } 158 | 159 | goto Lcleanup; 160 | 161 | Lerror: 162 | print_last_error(); 163 | 164 | Lfailure: 165 | exitCode = 1; 166 | 167 | Lcleanup: 168 | cosim_manipulator_destroy(manipulator); 169 | cosim_observer_destroy(observer); 170 | cosim_local_slave_destroy(slave); 171 | cosim_execution_destroy(execution); 172 | 173 | return exitCode; 174 | } 175 | -------------------------------------------------------------------------------- /tests/time_series_observer_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | void print_last_error() 16 | { 17 | fprintf( 18 | stderr, 19 | "Error code %d: %s\n", 20 | cosim_last_error_code(), cosim_last_error_message()); 21 | } 22 | 23 | int main() 24 | { 25 | int exitCode = 0; 26 | 27 | cosim_execution* execution = NULL; 28 | cosim_slave* slave = NULL; 29 | cosim_observer* observer = NULL; 30 | cosim_manipulator* manipulator = NULL; 31 | 32 | const char* dataDir = getenv("TEST_DATA_DIR"); 33 | if (!dataDir) { 34 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 35 | goto Lfailure; 36 | } 37 | 38 | char fmuPath[1024]; 39 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 40 | if (rc < 0) { 41 | perror(NULL); 42 | goto Lfailure; 43 | } 44 | 45 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 46 | execution = cosim_execution_create(0, nanoStepSize); 47 | if (!execution) { goto Lerror; } 48 | 49 | slave = cosim_local_slave_create(fmuPath, "slave"); 50 | if (!slave) { goto Lerror; } 51 | 52 | observer = cosim_time_series_observer_create(); 53 | if (!observer) { goto Lerror; } 54 | 55 | cosim_slave_index slaveIndex = cosim_execution_add_slave(execution, slave); 56 | if (slaveIndex < 0) { goto Lerror; } 57 | 58 | rc = cosim_execution_add_observer(execution, observer); 59 | if (rc < 0) { goto Lerror; } 60 | 61 | double inputRealSamples[10] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; 62 | int inputIntSamples[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 63 | 64 | cosim_value_reference reference = 0; 65 | 66 | rc = cosim_observer_start_observing(observer, 0, COSIM_VARIABLE_TYPE_INTEGER, reference); 67 | if (rc < 0) { goto Lerror; } 68 | 69 | manipulator = cosim_override_manipulator_create(); 70 | if (!manipulator) { goto Lerror; } 71 | 72 | rc = cosim_execution_add_manipulator(execution, manipulator); 73 | if (rc < 0) { goto Lerror; } 74 | 75 | for (int i = 0; i < 5; i++) { 76 | rc = cosim_manipulator_slave_set_real(manipulator, 0, &reference, 1, &inputRealSamples[i]); 77 | if (rc < 0) { goto Lerror; } 78 | rc = cosim_manipulator_slave_set_integer(manipulator, 0, &reference, 1, &inputIntSamples[i]); 79 | if (rc < 0) { goto Lerror; } 80 | rc = cosim_execution_step(execution, 1); 81 | if (rc < 0) { goto Lerror; } 82 | } 83 | 84 | rc = cosim_observer_stop_observing(observer, 0, COSIM_VARIABLE_TYPE_INTEGER, reference); 85 | if (rc < 0) { goto Lerror; } 86 | rc = cosim_observer_start_observing(observer, 0, COSIM_VARIABLE_TYPE_REAL, reference); 87 | if (rc < 0) { goto Lerror; } 88 | 89 | for (int i = 5; i < 10; i++) { 90 | rc = cosim_manipulator_slave_set_real(manipulator, 0, &reference, 1, &inputRealSamples[i]); 91 | if (rc < 0) { goto Lerror; } 92 | rc = cosim_manipulator_slave_set_integer(manipulator, 0, &reference, 1, &inputIntSamples[i]); 93 | if (rc < 0) { goto Lerror; } 94 | rc = cosim_execution_step(execution, 1); 95 | if (rc < 0) { goto Lerror; } 96 | } 97 | 98 | cosim_step_number fromStep = 1; 99 | const size_t nSamples = 10; 100 | double realSamples[10]; 101 | int intSamples[10]; 102 | cosim_time_point times[10]; 103 | cosim_step_number steps[10]; 104 | 105 | int64_t readRealSamples = cosim_observer_slave_get_real_samples(observer, slaveIndex, reference, fromStep, nSamples, realSamples, steps, times); 106 | if (readRealSamples != 5) { 107 | fprintf(stderr, "Expected to read 5 real samples, got %" PRId64 "\n", readRealSamples); 108 | goto Lfailure; 109 | } 110 | 111 | int64_t readIntSamples = cosim_observer_slave_get_integer_samples(observer, slaveIndex, reference, fromStep, nSamples, intSamples, steps, times); 112 | if (readIntSamples != 0) { 113 | fprintf(stderr, "Expected to read 0 int samples, got %" PRId64 "\n", readIntSamples); 114 | goto Lfailure; 115 | } 116 | 117 | goto Lcleanup; 118 | 119 | Lerror: 120 | print_last_error(); 121 | 122 | Lfailure: 123 | exitCode = 1; 124 | 125 | Lcleanup: 126 | cosim_manipulator_destroy(manipulator); 127 | cosim_observer_destroy(observer); 128 | cosim_local_slave_destroy(slave); 129 | cosim_execution_destroy(execution); 130 | 131 | return exitCode; 132 | } 133 | -------------------------------------------------------------------------------- /tests/variable_metadata_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WINDOWS 9 | # include 10 | #else 11 | # include 12 | # define Sleep(x) usleep((x)*1000) 13 | #endif 14 | 15 | void print_last_error() 16 | { 17 | fprintf( 18 | stderr, 19 | "Error code %d: %s\n", 20 | cosim_last_error_code(), cosim_last_error_message()); 21 | } 22 | 23 | int main() 24 | { 25 | int exitCode = 0; 26 | 27 | cosim_execution* execution = NULL; 28 | cosim_slave* slave = NULL; 29 | 30 | const char* dataDir = getenv("TEST_DATA_DIR"); 31 | if (!dataDir) { 32 | fprintf(stderr, "Environment variable TEST_DATA_DIR not set\n"); 33 | goto Lfailure; 34 | } 35 | 36 | char fmuPath[1024]; 37 | int rc = snprintf(fmuPath, sizeof fmuPath, "%s/fmi1/identity.fmu", dataDir); 38 | if (rc < 0) { 39 | perror(NULL); 40 | goto Lfailure; 41 | } 42 | 43 | // ===== Can step n times and get status 44 | int64_t nanoStepSize = (int64_t)(0.1 * 1.0e9); 45 | execution = cosim_execution_create(0, nanoStepSize); 46 | if (!execution) { goto Lerror; } 47 | 48 | slave = cosim_local_slave_create(fmuPath, "slave"); 49 | if (!slave) { goto Lerror; } 50 | 51 | cosim_slave_index slaveIndex = cosim_execution_add_slave(execution, slave); 52 | if (slaveIndex < 0) { goto Lerror; } 53 | 54 | size_t nVar = cosim_slave_get_num_variables(execution, slaveIndex); 55 | if (nVar != 8) { 56 | fprintf(stderr, "Expected 8 variables, got %zu\n", nVar); 57 | goto Lfailure; 58 | } 59 | 60 | cosim_variable_description vd[8]; 61 | 62 | rc = cosim_slave_get_variables(execution, slaveIndex, &vd[0], nVar); 63 | if (rc < 0) { 64 | print_last_error(); 65 | goto Lerror; 66 | } 67 | 68 | for (size_t i = 0; i < nVar; i++) { 69 | if (0 == strncmp(vd[i].name, "stringOut", SLAVE_NAME_MAX_SIZE)) { 70 | if (vd[i].causality != COSIM_VARIABLE_CAUSALITY_OUTPUT) { 71 | fprintf(stderr, "Expected causality to be output\n"); 72 | goto Lfailure; 73 | } 74 | if (vd[i].variability != COSIM_VARIABLE_VARIABILITY_DISCRETE) { 75 | fprintf(stderr, "Expected variability to be discrete\n"); 76 | goto Lfailure; 77 | } 78 | if (vd[i].type != COSIM_VARIABLE_TYPE_STRING) { 79 | fprintf(stderr, "Expected type to be string\n"); 80 | goto Lfailure; 81 | } 82 | if (vd[i].reference != 0) { 83 | fprintf(stderr, "Expected variable reference to be 0, got %i\n", vd[i].reference); 84 | goto Lfailure; 85 | } 86 | } 87 | if (0 == strncmp(vd[i].name, "realIn", SLAVE_NAME_MAX_SIZE)) { 88 | if (vd[i].causality != COSIM_VARIABLE_CAUSALITY_INPUT) { 89 | fprintf(stderr, "Expected causality to be input\n"); 90 | goto Lfailure; 91 | } 92 | if (vd[i].variability != COSIM_VARIABLE_VARIABILITY_DISCRETE) { 93 | fprintf(stderr, "Expected variability to be discrete\n"); 94 | goto Lfailure; 95 | } 96 | if (vd[i].type != COSIM_VARIABLE_TYPE_REAL) { 97 | fprintf(stderr, "Expected type to be real\n"); 98 | goto Lfailure; 99 | } 100 | if (vd[i].reference != 0) { 101 | fprintf(stderr, "Expected variable reference to be 0, got %i\n", vd[i].reference); 102 | goto Lfailure; 103 | } 104 | } 105 | } 106 | 107 | 108 | goto Lcleanup; 109 | 110 | Lerror: 111 | print_last_error(); 112 | 113 | Lfailure: 114 | exitCode = 1; 115 | 116 | Lcleanup: 117 | cosim_local_slave_destroy(slave); 118 | cosim_execution_destroy(execution); 119 | 120 | return exitCode; 121 | } 122 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | 0.11.1 2 | --------------------------------------------------------------------------------