diff --git a/.gitignore b/.gitignore index b5ed9b2..7775be0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ fmi1_cs/ fmi1_me/ fmi2/ fmi3/ +fmus/ +*.fmu +*.zip diff --git a/BouncingBall/FMI1CS.xml b/BouncingBall/FMI1CS.xml index c26d9f8..ae512ad 100644 --- a/BouncingBall/FMI1CS.xml +++ b/BouncingBall/FMI1CS.xml @@ -1,5 +1,12 @@ - + diff --git a/BouncingBall/FMI1ME.xml b/BouncingBall/FMI1ME.xml index f9a3f48..3cacebb 100644 --- a/BouncingBall/FMI1ME.xml +++ b/BouncingBall/FMI1ME.xml @@ -1,19 +1,26 @@ - + - + - + - + - + diff --git a/BouncingBall/FMI2.xml b/BouncingBall/FMI2.xml index cea915b..8bc3c79 100644 --- a/BouncingBall/FMI2.xml +++ b/BouncingBall/FMI2.xml @@ -1,26 +1,23 @@ - + - - - - + - - - - + - - @@ -45,22 +42,27 @@ - + + + + + + - + - + - + - + - + diff --git a/BouncingBall/FMI3.xml b/BouncingBall/FMI3.xml index 230887a..8a7273f 100644 --- a/BouncingBall/FMI3.xml +++ b/BouncingBall/FMI3.xml @@ -3,24 +3,22 @@ - + - + - - - - + + - + diff --git a/BouncingBall/config.h b/BouncingBall/config.h index c5bd308..82b382b 100644 --- a/BouncingBall/config.h +++ b/BouncingBall/config.h @@ -13,6 +13,8 @@ #define SET_FLOAT64 #define EVENT_UPDATE +#define FIXED_SOLVER_STEP 1e-3 + typedef enum { vr_h, vr_v, vr_g, vr_e, vr_v_min } ValueReference; diff --git a/BouncingBall/model.c b/BouncingBall/model.c index 9656b36..4c5046e 100644 --- a/BouncingBall/model.c +++ b/BouncingBall/model.c @@ -42,15 +42,15 @@ Status getFloat64(ModelInstance* comp, ValueReference vr, double *value, size_t Status setFloat64(ModelInstance* comp, ValueReference vr, const double *value, size_t *index) { switch (vr) { - + case vr_h: M(h) = value[(*index)++]; return OK; - + case vr_v: M(v) = value[(*index)++]; return OK; - + case vr_g: #if FMI_VERSION > 1 if (comp->type == ModelExchange && @@ -62,7 +62,7 @@ Status setFloat64(ModelInstance* comp, ValueReference vr, const double *value, s #endif M(g) = value[(*index)++]; return OK; - + case vr_e: #if FMI_VERSION > 1 if (comp->type == ModelExchange && @@ -75,8 +75,13 @@ Status setFloat64(ModelInstance* comp, ValueReference vr, const double *value, s #endif M(e) = value[(*index)++]; return OK; - + + case vr_v_min: + logError(comp, "Variable v_min (value reference %d) is constant and cannot be set.", vr_v_min); + return Error; + default: + logError(comp, "Unexpected value reference: %.", vr); return Error; } } diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cecbe6..f57f764 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ if (${FMI_VERSION} GREATER 1) set(FMI_TYPE "") endif () -if (WIN32) +if (MSVC) string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") string(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") endif () @@ -58,6 +58,13 @@ endif () MESSAGE("FMI_PLATFORM: " ${FMI_PLATFORM}) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/all.c" "#define FMI_VERSION ${FMI_VERSION} + +#include \"fmi${FMI_VERSION}Functions.c\" +#include \"model.c\" +#include \"slave.c\" +") + set (MODEL_NAMES BouncingBall Dahlquist Stair Feedthrough VanDerPol) if (${FMI_VERSION} GREATER 1 OR "${FMI_TYPE}" STREQUAL "CS") @@ -75,13 +82,11 @@ set(TARGET_NAME ${MODEL_NAME}) SET(HEADERS ${MODEL_NAME}/config.h include/model.h - include/solver.h ) SET(SOURCES ${MODEL_NAME}/model.c - src/fmi${FMI_VERSION}.c - src/euler.c + src/fmi${FMI_VERSION}Functions.c src/slave.c ) @@ -93,7 +98,11 @@ add_library(${TARGET_NAME} SHARED file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dist) -target_compile_definitions(${TARGET_NAME} PRIVATE DISABLE_PREFIX FMI_VERSION=${FMI_VERSION}) +target_compile_definitions(${TARGET_NAME} PRIVATE + _CRT_SECURE_NO_WARNINGS + DISABLE_PREFIX + FMI_VERSION=${FMI_VERSION} +) if (${FMI_VERSION} EQUAL 1 AND "${FMI_TYPE}" STREQUAL CS) target_compile_definitions(${TARGET_NAME} PRIVATE FMI_COSIMULATION) @@ -163,7 +172,7 @@ add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E ) # common headers -foreach (SOURCE_FILE model.h slave.h solver.h) +foreach (SOURCE_FILE model.h slave.h) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/include/${SOURCE_FILE}" "${FMU_BUILD_DIR}/sources/${SOURCE_FILE}" @@ -171,13 +180,19 @@ foreach (SOURCE_FILE model.h slave.h solver.h) endforeach(SOURCE_FILE) # common sources -foreach (SOURCE_FILE fmi${FMI_VERSION}.c euler.c slave.c) +foreach (SOURCE_FILE fmi${FMI_VERSION}Functions.c slave.c) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/src/${SOURCE_FILE}" "${FMU_BUILD_DIR}/sources/${SOURCE_FILE}" ) endforeach(SOURCE_FILE) +# all.c +add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy + "${CMAKE_CURRENT_BINARY_DIR}/all.c" + "${FMU_BUILD_DIR}/sources/all.c" +) + set(ARCHIVE_FILES "modelDescription.xml" "binaries" "documentation" "sources") if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_NAME}/resources") diff --git a/Dahlquist/FMI1CS.xml b/Dahlquist/FMI1CS.xml index 6184a42..806a349 100644 --- a/Dahlquist/FMI1CS.xml +++ b/Dahlquist/FMI1CS.xml @@ -1,5 +1,12 @@ - + diff --git a/Dahlquist/FMI1ME.xml b/Dahlquist/FMI1ME.xml index 91da018..8be2932 100644 --- a/Dahlquist/FMI1ME.xml +++ b/Dahlquist/FMI1ME.xml @@ -1,5 +1,12 @@ - + diff --git a/Dahlquist/FMI2.xml b/Dahlquist/FMI2.xml index 4058249..2182179 100644 --- a/Dahlquist/FMI2.xml +++ b/Dahlquist/FMI2.xml @@ -1,32 +1,29 @@ - + - - - - + - - - - + - - - - + + - + diff --git a/Dahlquist/FMI3.xml b/Dahlquist/FMI3.xml index 4e57dd3..1b42106 100644 --- a/Dahlquist/FMI3.xml +++ b/Dahlquist/FMI3.xml @@ -3,30 +3,22 @@ - - - - + - - - - + - - - - + + - + diff --git a/Dahlquist/config.h b/Dahlquist/config.h index 43c4bbf..52bb1da 100644 --- a/Dahlquist/config.h +++ b/Dahlquist/config.h @@ -13,6 +13,8 @@ #define SET_FLOAT64 #define EVENT_UPDATE +#define FIXED_SOLVER_STEP 0.1 + typedef enum { vr_x, vr_der_x, vr_k } ValueReference; diff --git a/Feedthrough/FMI1CS.xml b/Feedthrough/FMI1CS.xml index 7ad2fdc..e23eb2e 100644 --- a/Feedthrough/FMI1CS.xml +++ b/Feedthrough/FMI1CS.xml @@ -1,5 +1,12 @@ - + diff --git a/Feedthrough/FMI1ME.xml b/Feedthrough/FMI1ME.xml index 67d8cbd..445784e 100644 --- a/Feedthrough/FMI1ME.xml +++ b/Feedthrough/FMI1ME.xml @@ -1,5 +1,12 @@ - + diff --git a/Feedthrough/FMI2.xml b/Feedthrough/FMI2.xml index 1148851..2c0979c 100644 --- a/Feedthrough/FMI2.xml +++ b/Feedthrough/FMI2.xml @@ -1,29 +1,26 @@ - + - - - - + - - - - + - - - - + + diff --git a/Feedthrough/FMI3.xml b/Feedthrough/FMI3.xml index 57a999f..341685b 100644 --- a/Feedthrough/FMI3.xml +++ b/Feedthrough/FMI3.xml @@ -3,27 +3,19 @@ - - - - + - - - - + - - - - + + diff --git a/Feedthrough/config.h b/Feedthrough/config.h index a54f8a1..01d8fb3 100644 --- a/Feedthrough/config.h +++ b/Feedthrough/config.h @@ -24,6 +24,8 @@ #define EVENT_UPDATE +#define FIXED_SOLVER_STEP 1 + typedef enum { vr_fixed_real_parameter, vr_tunable_real_parameter, diff --git a/LinearTransform/FMI3.xml b/LinearTransform/FMI3.xml index 810a08f..df348d2 100644 --- a/LinearTransform/FMI3.xml +++ b/LinearTransform/FMI3.xml @@ -3,27 +3,19 @@ - - - - + - - - - + - - - - + + diff --git a/LinearTransform/config.h b/LinearTransform/config.h index 2b359de..057fc5d 100644 --- a/LinearTransform/config.h +++ b/LinearTransform/config.h @@ -13,6 +13,8 @@ #define SET_FLOAT64 #define EVENT_UPDATE +#define FIXED_SOLVER_STEP 1 + typedef enum { vr_m, vr_n, vr_u, vr_A, vr_y } ValueReference; diff --git a/README.md b/README.md index 8923f7b..d17b3e6 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ # Test FMUs +[![Build Status](https://dev.azure.com/CATIA-Systems/Test-FMUs/_apis/build/status/CATIA-Systems.Test-FMUs?branchName=develop)](https://dev.azure.com/CATIA-Systems/Test-FMUs/_build/latest?definitionId=2&branchName=develop) + A set of test models for development, testing and debugging of the [Functional Mock-up Interface](https://fmi-standard.org/). - [BouncingBall](BouncingBall) - a bouncing ball model with state events @@ -17,18 +19,16 @@ A set of test models for development, testing and debugging of the [Functional M `` - `config.h` - model specific types and definitions -- `FMI*.xml` - model descriptions +- `FMI{1CS|1ME|2|3}.xml` - model descriptions - `model.c` - implementation of the model `include` -- `fmi*.h` - FMI header files +- `fmi{|2|3}Functions.h` - FMI header files - `model.h` - generic model interface - `slave.h` - generic co-simulation interface -- `solver.h` - solver interface `src` -- `euler.c` - forward Euler solver -- `fmi[1,2,3].c` - FMI implementation +- `fmi{1|2|3}Functions.c` - FMI implementations - `slave.c` - generic co-simulation ## Build the FMUs diff --git a/Resource/FMI1CS.xml b/Resource/FMI1CS.xml index 1dbba64..48ded3c 100644 --- a/Resource/FMI1CS.xml +++ b/Resource/FMI1CS.xml @@ -1,10 +1,17 @@ - + - + diff --git a/Resource/FMI1ME.xml b/Resource/FMI1ME.xml index cfd4092..4605c0b 100644 --- a/Resource/FMI1ME.xml +++ b/Resource/FMI1ME.xml @@ -1,10 +1,17 @@ - + - + diff --git a/Resource/FMI2.xml b/Resource/FMI2.xml index d495fe6..85601d7 100644 --- a/Resource/FMI2.xml +++ b/Resource/FMI2.xml @@ -1,35 +1,32 @@ - + - - - - + - - - - + - - - - + + - + diff --git a/Resource/FMI3.xml b/Resource/FMI3.xml index 6a54b14..ab0ff16 100644 --- a/Resource/FMI3.xml +++ b/Resource/FMI3.xml @@ -3,33 +3,25 @@ - - - - + - - - - + - - - - + + - + diff --git a/Resource/config.h b/Resource/config.h index 1e52d53..4e509a7 100644 --- a/Resource/config.h +++ b/Resource/config.h @@ -11,6 +11,8 @@ #define GET_FLOAT64 +#define FIXED_SOLVER_STEP 1 + typedef enum { vr_y } ValueReference; diff --git a/Resource/model.c b/Resource/model.c index 222ef3f..f695e19 100644 --- a/Resource/model.c +++ b/Resource/model.c @@ -12,15 +12,15 @@ void setStartValues(ModelInstance *comp) { void calculateValues(ModelInstance *comp) { // load the file - FILE *file; - char *path; - char c; + FILE *file = NULL; + char *path = NULL; + char c = '\0'; const char *scheme1 = "file:///"; const char *scheme2 = "file:/"; #if FMI_VERSION < 2 - char *resourcePath = "/resources/y.txt"; + const char *resourcePath = "/resources/y.txt"; #else - char *resourcePath = "/y.txt"; + const char *resourcePath = "/y.txt"; #endif if (!comp->resourceLocation) { @@ -31,15 +31,16 @@ void calculateValues(ModelInstance *comp) { if (strncmp(comp->resourceLocation, scheme1, strlen(scheme1)) == 0) { path = malloc(strlen(comp->resourceLocation) + strlen(resourcePath) + 1); strcpy(path, &comp->resourceLocation[strlen(scheme1)] - 1); - strcat(path, resourcePath); } else if (strncmp(comp->resourceLocation, scheme2, strlen(scheme2)) == 0) { path = malloc(strlen(comp->resourceLocation) + strlen(resourcePath) + 1); strcpy(path, &comp->resourceLocation[strlen(scheme2) - 1]); - } else { + } else { logError(comp, "The resourceLocation must start with \"file:/\" or \"file:///\""); return; } + strcat(path, resourcePath); + #ifdef _WIN32 // strip any leading slashes while (path[0] == '/') { @@ -61,8 +62,11 @@ void calculateValues(ModelInstance *comp) { // assign it to y comp->modelData->y = c; - // clost the file + // close the file fclose(file); + + // clean up + free(path); } Status getFloat64(ModelInstance* comp, ValueReference vr, double *value, size_t *index) { diff --git a/Stair/FMI1CS.xml b/Stair/FMI1CS.xml index f451379..27cff66 100644 --- a/Stair/FMI1CS.xml +++ b/Stair/FMI1CS.xml @@ -1,5 +1,12 @@ - + diff --git a/Stair/FMI1ME.xml b/Stair/FMI1ME.xml index 484bc1e..1a938c9 100644 --- a/Stair/FMI1ME.xml +++ b/Stair/FMI1ME.xml @@ -1,5 +1,12 @@ - + diff --git a/Stair/FMI2.xml b/Stair/FMI2.xml index 13afed4..a5ff4a4 100644 --- a/Stair/FMI2.xml +++ b/Stair/FMI2.xml @@ -1,32 +1,29 @@ - + - - - - + - - - - + - - - - + + - + diff --git a/Stair/FMI3.xml b/Stair/FMI3.xml index d72be8c..bc439d6 100644 --- a/Stair/FMI3.xml +++ b/Stair/FMI3.xml @@ -3,30 +3,22 @@ - - - - + - - - - + - - - - + + - + diff --git a/Stair/config.h b/Stair/config.h index f5aafa5..ed1ceae 100644 --- a/Stair/config.h +++ b/Stair/config.h @@ -12,13 +12,15 @@ #define GET_INT32 #define EVENT_UPDATE +#define FIXED_SOLVER_STEP 0.2 + typedef enum { vr_counter } ValueReference; typedef struct { - double counter; + int counter; } ModelData; diff --git a/Stair/model.c b/Stair/model.c index 6b9fca0..03dddc5 100644 --- a/Stair/model.c +++ b/Stair/model.c @@ -26,7 +26,7 @@ Status getInt32(ModelInstance* comp, ValueReference vr, int *value, size_t *inde void eventUpdate(ModelInstance *comp) { - if (comp->nextEventTimeDefined && comp->nextEventTime == comp->time) { + if (comp->nextEventTimeDefined && comp->time >= comp->nextEventTime) { M(counter)++; comp->nextEventTime += 1; } diff --git a/VanDerPol/FMI1CS.xml b/VanDerPol/FMI1CS.xml index c316805..aa5f005 100644 --- a/VanDerPol/FMI1CS.xml +++ b/VanDerPol/FMI1CS.xml @@ -1,5 +1,12 @@ - + diff --git a/VanDerPol/FMI1ME.xml b/VanDerPol/FMI1ME.xml index 4cfc35c..305e170 100644 --- a/VanDerPol/FMI1ME.xml +++ b/VanDerPol/FMI1ME.xml @@ -1,5 +1,12 @@ - + diff --git a/VanDerPol/FMI2.xml b/VanDerPol/FMI2.xml index 4fb5005..eb8e77a 100644 --- a/VanDerPol/FMI2.xml +++ b/VanDerPol/FMI2.xml @@ -1,32 +1,29 @@ - + - - - - + - - - - + - - - - + + - + diff --git a/VanDerPol/FMI3.xml b/VanDerPol/FMI3.xml index 5ee6162..b55eb36 100644 --- a/VanDerPol/FMI3.xml +++ b/VanDerPol/FMI3.xml @@ -3,30 +3,22 @@ - - - - + - - - - + - - - - + + - + diff --git a/VanDerPol/config.h b/VanDerPol/config.h index 636bca8..fa16b17 100644 --- a/VanDerPol/config.h +++ b/VanDerPol/config.h @@ -12,6 +12,8 @@ #define GET_FLOAT64 #define SET_FLOAT64 +#define FIXED_SOLVER_STEP 1e-2 + typedef enum { vr_x0, vr_der_x0, vr_x1, vr_der_x1, vr_mu } ValueReference; diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 73cba9d..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,9 +0,0 @@ -image: Visual Studio 2015 - -build: false - -install: - - pip install fmpy - -test_script: - - python test_build.py diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..918d4a9 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,145 @@ +jobs: + +- job: linux64 + displayName: 'Ubuntu 16.04' + pool: + vmImage: 'ubuntu-16.04' + + steps: + + - bash: echo "##vso[task.prependpath]$CONDA/bin" + displayName: Add conda to PATH + + - bash: conda create --yes --quiet --name myEnvironment + displayName: Create Anaconda environment + + - bash: | + source activate myEnvironment + conda install --yes --quiet --name myEnvironment -c conda-forge python=3.7 fmpy pytest scipy + displayName: Install Anaconda packages + + - bash: | + source activate myEnvironment + pytest --junitxml=junit/test-results.xml + displayName: Build and run tests + + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + testRunTitle: 'Publish test results' + + - task: PublishPipelineArtifact@1 + inputs: + path: fmus + artifact: linux64 + +- job: darwin64 + displayName: 'macOS 10.13' + pool: + vmImage: 'macos-10.13' + + steps: + + - bash: echo "##vso[task.prependpath]$CONDA/bin" + displayName: Add conda to PATH + + # On Hosted macOS, the agent user doesn't have ownership of Miniconda's installation directory/ + # We need to take ownership if we want to update conda or install packages globally + - bash: sudo chown -R $USER $CONDA + displayName: Take ownership of conda installation + + - bash: conda create --yes --quiet --name myEnvironment + displayName: Create Anaconda environment + + - bash: | + source activate myEnvironment + conda install --yes --quiet --name myEnvironment -c conda-forge python=3.7 fmpy pytest scipy + displayName: Install Anaconda packages + + - bash: | + source activate myEnvironment + pytest --junitxml=junit/test-results.xml + displayName: Build and run tests + + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + testRunTitle: 'Publish test results' + + - task: PublishPipelineArtifact@1 + inputs: + path: fmus + artifact: darwin64 + +- job: win64 + displayName: 'Windows 2016' + pool: + vmImage: 'vs2017-win2016' + + steps: + + - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" + displayName: Add conda to PATH + + - script: conda create --yes --quiet --name myEnvironment + displayName: Create Anaconda environment + + - script: | + call activate myEnvironment + conda install --yes --quiet --name myEnvironment -c conda-forge python=3.7 cmake fmpy pytest scipy + displayName: Install Anaconda packages + + - script: | + call activate myEnvironment + pytest --junitxml=junit/test-results.xml + displayName: Build, install and run tests + + - task: PublishTestResults@2 + condition: succeededOrFailed() + inputs: + testResultsFiles: '**/test-*.xml' + testRunTitle: 'Publish test results' + + - task: PublishPipelineArtifact@1 + inputs: + path: fmus + artifact: win64 + +- job: merge + dependsOn: + - linux64 + - darwin64 + - win64 + displayName: 'Merge platform binaries' + pool: + vmImage: 'ubuntu-16.04' + + steps: + + - bash: mkdir fmus + displayName: Create fmus directory + + - task: DownloadPipelineArtifact@2 + inputs: + artifact: linux64 + downloadPath: fmus + + - task: DownloadPipelineArtifact@2 + inputs: + artifact: darwin64 + downloadPath: fmus + + - task: DownloadPipelineArtifact@2 + inputs: + artifact: win64 + downloadPath: fmus + + - bash: python3 merge_platform_binaries.py + displayName: Merge FMUs + + - task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: merged_fmus.zip + artifactName: merged_fmus diff --git a/include/model.h b/include/model.h index 82b033c..787ad54 100644 --- a/include/model.h +++ b/include/model.h @@ -6,15 +6,6 @@ #include "config.h" -// categories of logging supported by model. -// Value is the index in logCategories of a ModelInstance. -#define LOG_ALL 0 -#define LOG_ERROR 1 -#define LOG_FMI_CALL 2 -#define LOG_EVENT 3 - -#define NUMBER_OF_CATEGORIES 4 - #if FMI_VERSION == 1 #define not_modelError (modelInstantiated|modelInitialized|modelTerminated) @@ -64,29 +55,30 @@ typedef enum { Pending } Status; +#if FMI_VERSION < 3 +typedef void (*loggerType) (void *componentEnvironment, const char *instanceName, int status, const char *category, const char *message, ...); +typedef void* (*allocateMemoryType)(size_t nobj, size_t size); +typedef void (*freeMemoryType) (void *obj); +#else +typedef void (*loggerType) (void *componentEnvironment, const char *instanceName, int status, const char *category, const char *message); +typedef void* (*allocateMemoryType)(void *componentEnvironment, size_t nobj, size_t size); +typedef void (*freeMemoryType) (void *componentEnvironment, void *obj); +#endif typedef struct { double time; const char *instanceName; InterfaceType type; - const char *GUID; const char *resourceLocation; - // callback functions -#if FMI_VERSION < 3 - void (*logger)(void *, const char *, int, const char *, const char *, ...); - void* (*allocateMemory)(size_t, size_t); - void (*freeMemory)(void *); - void (*stepFinished)(void *, int); -#else - void (*logger)(void *, const char *, int, const char *, const char *, ...); - void* (*allocateMemory)(void *, size_t, size_t); - void (*freeMemory)(void *, void *); - void (*stepFinished)(void *, void *, int); -#endif - bool loggingOn; - bool logCategories[NUMBER_OF_CATEGORIES]; + // callback functions + loggerType logger; + allocateMemoryType allocateMemory; + freeMemoryType freeMemory; + + bool logEvents; + bool logErrors; void *componentEnvironment; ModelState state; @@ -103,10 +95,25 @@ typedef struct { bool isNewEventIteration; ModelData *modelData; - void *solverData; + + // event indicators + double *z; + double *prez; } ModelInstance; +ModelInstance *createModelInstance( + loggerType logger, + allocateMemoryType allocateMemory, + freeMemoryType freeMemory, + void *componentEnvironment, + const char *instanceName, + const char *GUID, + const char *resourceLocation, + bool loggingOn, + InterfaceType interfaceType); +void freeModelInstance(ModelInstance *comp); + void setStartValues(ModelInstance *comp); void calculateValues(ModelInstance *comp); @@ -126,10 +133,16 @@ void getDerivatives(ModelInstance *comp, double dx[], size_t nx); void getEventIndicators(ModelInstance *comp, double z[], size_t nz); void eventUpdate(ModelInstance *comp); -void logError(ModelInstance *comp, const char *message, ...); -void *allocateMemory(ModelInstance *comp, size_t size); +void *allocateMemory(ModelInstance *comp, size_t num, size_t size); void freeMemory(ModelInstance *comp, void *obj); const char *duplicateString(ModelInstance *comp, const char *str1); +bool invalidNumber(ModelInstance *comp, const char *f, const char *arg, size_t actual, size_t expected); +bool invalidState(ModelInstance *comp, const char *f, int statesExpected); +bool nullPointer(ModelInstance* comp, const char *f, const char *arg, const void *p); +void logError(ModelInstance *comp, const char *message, ...); +Status setDebugLogging(ModelInstance *comp, bool loggingOn, size_t nCategories, const char * const categories[]); +void logEvent(ModelInstance *comp, const char *message, ...); +void logError(ModelInstance *comp, const char *message, ...); // shorthand to access the variables #define M(v) (comp->modelData->v) diff --git a/include/solver.h b/include/solver.h deleted file mode 100644 index 3740f36..0000000 --- a/include/solver.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef solver_h -#define solver_h - -#include "config.h" -#include "model.h" - - -void *solver_create(ModelInstance *comp); -void solver_step(ModelInstance *comp, double t, double tNext, double *tRet, int *stateEvent); -void solver_reset(ModelInstance *comp); - - -#endif /* solver_h */ diff --git a/merge_platform_binaries.py b/merge_platform_binaries.py new file mode 100644 index 0000000..830057a --- /dev/null +++ b/merge_platform_binaries.py @@ -0,0 +1,56 @@ +import zipfile +from tempfile import mkdtemp +from shutil import rmtree +import shutil +import os + +repo_dir = os.path.dirname(__file__) + +test_fmus_version = '0.0.2' + + +def merge(fmi_version, fmi_types): + + for fmi_type in fmi_types: + + platforms_dir = os.path.join(repo_dir, 'fmus', fmi_version, fmi_type) + + _, platforms, _ = next(os.walk(platforms_dir)) + + version_dir = os.path.join(repo_dir, 'fmus', fmi_version, fmi_type, platforms[0], 'Test-FMUs', test_fmus_version) + + _, model_names, _ = next(os.walk(version_dir)) + + for model_name in model_names: + + tempdir = mkdtemp() + + for platform in platforms: + + platform_fmu = os.path.join(repo_dir, 'fmus', fmi_version, fmi_type, platform, 'Test-FMUs', test_fmus_version, model_name, model_name + '.fmu') + + if not os.path.isfile(platform_fmu): + continue + + with zipfile.ZipFile(platform_fmu, 'r') as archive: + archive.extractall(path=tempdir) + + if len(fmi_types) > 1: + merged_fmu = os.path.join(repo_dir, 'merged', fmi_version, fmi_type, model_name + '.fmu') + else: + merged_fmu = os.path.join(repo_dir, 'merged', fmi_version, model_name + '.fmu') + + os.makedirs(os.path.dirname(merged_fmu), exist_ok=True) + + shutil.make_archive(merged_fmu, 'zip', tempdir) + + os.rename(merged_fmu + '.zip', merged_fmu) + + rmtree(tempdir, ignore_errors=True) + + +merge('1.0', ['cs', 'me']) +merge('2.0', ['cs']) +merge('3.0', ['cs']) + +shutil.make_archive(os.path.join(repo_dir, 'merged_fmus'), 'zip', os.path.join(repo_dir, 'merged')) diff --git a/src/euler.c b/src/euler.c deleted file mode 100644 index d5b3bc5..0000000 --- a/src/euler.c +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************** - * Copyright (c) Dassault Systemes. All rights reserved. * - * This file is part of the Test-FMUs. See LICENSE.txt in the * - * project root for license information. * - ****************************************************************/ - -#include "solver.h" -#include - - -typedef struct { - int nx; - int nz; - double *x; - double *dx; - double *z; - double *prez; -} SolverData; - -void *solver_create(ModelInstance *comp) { - SolverData *s = (SolverData *)calloc(1, sizeof(SolverData)); - s->nx = NUMBER_OF_STATES; - s->nz = NUMBER_OF_EVENT_INDICATORS; - s->x = (double *)calloc(s->nx, sizeof(double)); - s->dx = (double *)calloc(s->nx, sizeof(double)); - s->z = (double *)calloc(s->nz, sizeof(double)); - s->prez = (double *)calloc(s->nz, sizeof(double)); - return s; -} - -void solver_step(ModelInstance *comp, double t, double tNext, double *tRet, int *stateEvent) { - - SolverData *s = (SolverData *)comp->solverData; - - // step size - const double h = tNext - t; - - double *temp; - - // set continuous states - getContinuousStates(comp, s->x, s->nx); - - // get derivatives - getDerivatives(comp, s->dx, s->nx); - - // forward Euler step - for (int i = 0; i < s->nx; i++) { - s->x[i] += h * s->dx[i]; - } - - // tNext has been reached - *tRet = tNext; - - // set continuous states - setContinuousStates(comp, s->x, s->nx); - - // get event indicators - getEventIndicators(comp, s->z, s->nz); - - *stateEvent = false; - - // check for zero-crossing - for (int i = 0; i < s->nz; i++) { - *stateEvent |= (s->prez[i] * s->z[i]) <= 0; - } - - // remember the current event indicators - temp = s->z; - s->z = s->prez; - s->prez = temp; -} - -void solver_reset(ModelInstance *comp) { - - SolverData *s = (SolverData *)comp->solverData; - - // set continuous states - setContinuousStates(comp, s->x, s->nx); - - // get event indicators - getEventIndicators(comp, s->z, s->nz); - -} diff --git a/src/fmi1.c b/src/fmi1Functions.c similarity index 66% rename from src/fmi1.c rename to src/fmi1Functions.c index 96dd423..916016c 100644 --- a/src/fmi1.c +++ b/src/fmi1Functions.c @@ -1,739 +1,578 @@ -/**************************************************************** - * Copyright (c) Dassault Systemes. All rights reserved. * - * This file is part of the Test-FMUs. See LICENSE.txt in the * - * project root for license information. * - ****************************************************************/ - -#include -#include -#include -#include - -#include "config.h" -#include "model.h" -#include "solver.h" -#include "slave.h" - - -#ifdef FMI_COSIMULATION -#include "fmiFunctions.h" -#else -#include "fmiModelFunctions.h" -#endif - -//// array of value references of states -//#if NUMBER_OF_STATES>0 -//fmiValueReference vrStates[NUMBER_OF_STATES] = STATES; -//#endif - -#ifndef max -#define max(a,b) ((a)>(b) ? (a) : (b)) -#endif - -#ifndef DT_EVENT_DETECT -#define DT_EVENT_DETECT 1e-10 -#endif - -// --------------------------------------------------------------------------- -// Private helpers used below to validate function arguments -// --------------------------------------------------------------------------- - -void logError(ModelInstance *comp, const char *message, ...) { - - va_list args; - size_t len = 0; - char *buf = ""; - - va_start(args, message); - len = vsnprintf(buf, len, message, args); - va_end(args); - - buf = malloc(len + 1); - - va_start(args, message); - len = vsnprintf(buf, len + 1, message, args); - va_end(args); - - comp->logger(comp->componentEnvironment, comp->instanceName, fmiError, "logError", buf); - - free(buf); -} - -void *allocateMemory(ModelInstance *comp, size_t size) { - return comp->allocateMemory(size, 1); -} - -void freeMemory(ModelInstance *comp, void *obj) { - comp->freeMemory(obj); -} - -const char *duplicateString(ModelInstance *comp, const char *str1) { - size_t len = strlen(str1); - char *str2 = allocateMemory(comp, len + 1); - strncpy(str2, str1, len + 1); - return str2; -} - -static fmiBoolean invalidNumber(ModelInstance* comp, const char* f, const char* arg, int n, int nExpected){ - if (n != nExpected) { - comp->state = modelError; - comp->logger(comp, comp->instanceName, fmiError, "error", - "%s: Invalid argument %s = %d. Expected %d.", f, arg, n, nExpected); - return fmiTrue; - } - return fmiFalse; -} - -static fmiBoolean invalidState(ModelInstance* comp, const char* f, int statesExpected){ - if (!comp) - return fmiTrue; - if (!(comp->state & statesExpected)) { - comp->state = modelError; - comp->logger(comp, comp->instanceName, fmiError, "error", - "%s: Illegal call sequence.", f); - return fmiTrue; - } - return fmiFalse; -} - -static fmiBoolean nullPointer(ModelInstance* comp, const char* f, const char* arg, const void* p){ - if (!p) { - comp->state = modelError; - comp->logger(comp, comp->instanceName, fmiError, "error", - "%s: Invalid argument %s = NULL.", f, arg); - return fmiTrue; - } - return fmiFalse; -} - -// --------------------------------------------------------------------------- -// Private helpers used below to implement functions -// --------------------------------------------------------------------------- - -// fname is fmiInstantiateModel or fmiInstantiateSlave -static fmiComponent instantiateModel(char* fname, fmiString instanceName, fmiString GUID, - fmiString fmuLocation, fmiCallbackFunctions functions, fmiBoolean loggingOn) { - - ModelInstance* comp; - - if (!functions.logger) - return NULL; // we cannot even log this problem - - if (!instanceName || strlen(instanceName) == 0) { - functions.logger(NULL, "?", fmiError, "error", - "%s: Missing instance name.", fname); - return NULL; - } - - if (!GUID || strlen(GUID) == 0) { - functions.logger(NULL, instanceName, fmiError, "error", - "%s: Missing GUID.", fname); - return NULL; - } - -#if CO_SIMULATION - if (!fmuLocation || strlen(fmuLocation) == 0) { - functions.logger(NULL, instanceName, fmiError, "error", - "%s: Missing fmuLocation.", fname); - return NULL; - } -#endif - - if (!functions.allocateMemory || !functions.freeMemory){ - functions.logger(NULL, instanceName, fmiError, "error", - "%s: Missing callback function.", fname); - return NULL; - } - - if (strcmp(GUID, MODEL_GUID)) { - functions.logger(NULL, instanceName, fmiError, "error", - "%s: Wrong GUID %s. Expected %s.", fname, GUID, MODEL_GUID); - return NULL; - } - - comp = (ModelInstance *)functions.allocateMemory(1, sizeof(ModelInstance)); - - if (comp) { - comp->instanceName = (char *)functions.allocateMemory(1 + strlen(instanceName), sizeof(char)); - comp->GUID = (char *)functions.allocateMemory(1 + strlen(GUID), sizeof(char)); -#ifdef FMI_COSIMULATION - comp->resourceLocation = (char *)functions.allocateMemory(1 + strlen(fmuLocation), sizeof(char)); -#else - comp->resourceLocation = NULL; -#endif - comp->modelData = (ModelData *)functions.allocateMemory(1, sizeof(ModelData)); - } - - if (!comp || !comp->instanceName || !comp->GUID) { - functions.logger(NULL, instanceName, fmiError, "error", - "%s: Out of memory.", fname); - return NULL; - } - - if (loggingOn) functions.logger(NULL, instanceName, fmiOK, "log", - "%s: GUID=%s", fname, GUID); - - strcpy((char *)comp->instanceName, (char *)instanceName); - strcpy((char *)comp->GUID, (char *)GUID); -#ifdef FMI_COSIMULATION - strcpy((char *)comp->resourceLocation, (char *)fmuLocation); -#endif - comp->logger = functions.logger; - comp->allocateMemory = functions.allocateMemory; - comp->freeMemory = functions.freeMemory; - comp->loggingOn = loggingOn; - comp->state = modelInstantiated; - - setStartValues(comp); // to be implemented by the includer of this file - - comp->solverData = solver_create(comp); - - return comp; -} - -// fname is fmiInitialize or fmiInitializeSlave -static fmiStatus init(fmiComponent c) { - ModelInstance* comp = (ModelInstance *)c; - comp->state = modelInitialized; - calculateValues(comp); - return fmiOK; -} - -// fname is fmiTerminate or fmiTerminateSlave -static fmiStatus terminate(char* fname, fmiComponent c) { - ModelInstance* comp = (ModelInstance *)c; - if (invalidState(comp, fname, modelInitialized)) - return fmiError; - if (comp->loggingOn) comp->logger(c, comp->instanceName, fmiOK, "log", fname); - comp->state = modelTerminated; - return fmiOK; -} - -// fname is freeModelInstance of freeSlaveInstance -void freeInstance(char* fname, fmiComponent c) { - ModelInstance* comp = (ModelInstance *)c; - if (!comp) return; - if (comp->loggingOn) comp->logger(c, comp->instanceName, fmiOK, "log", fname); - if (comp->instanceName) comp->freeMemory((void *)comp->instanceName); - if (comp->GUID) comp->freeMemory((void *)comp->GUID); - comp->freeMemory(comp); -} - -// --------------------------------------------------------------------------- -// FMI functions: class methods not depending of a specific model instance -// --------------------------------------------------------------------------- - -const char* fmiGetVersion() { - return fmiVersion; -} - -// --------------------------------------------------------------------------- -// FMI functions: for FMI Model Exchange 1.0 and for FMI Co-Simulation 1.0 -// logging control, setters and getters for Real, Integer, Boolean, String -// --------------------------------------------------------------------------- - -fmiStatus fmiSetDebugLogging(fmiComponent c, fmiBoolean loggingOn) { - ModelInstance* comp = (ModelInstance *)c; - if (invalidState(comp, "fmiSetDebugLogging", not_modelError)) - return fmiError; - if (comp->loggingOn) comp->logger(c, comp->instanceName, fmiOK, "log", - "fmiSetDebugLogging: loggingOn=%d", loggingOn); - comp->loggingOn = loggingOn; - return fmiOK; -} - -fmiStatus fmiSetReal(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiReal value[]) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiSetReal", modelInstantiated|modelInitialized)) - return fmiError; - - if (nvr>0 && nullPointer(comp, "fmiSetReal", "vr[]", vr)) - return fmiError; - - if (nvr>0 && nullPointer(comp, "fmiSetReal", "value[]", value)) - return fmiError; - - if (comp->loggingOn) comp->logger(c, comp->instanceName, fmiOK, "log", - "fmiSetReal: nvr = %d", nvr); - -#ifdef SET_FLOAT64 - SET_VARIABLES(Float64) -#else - return fmiError; // not implemented -#endif -} - -fmiStatus fmiSetInteger(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiInteger value[]) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiSetInteger", modelInstantiated|modelInitialized)) - return fmiError; - - if (nvr>0 && nullPointer(comp, "fmiSetInteger", "vr[]", vr)) - return fmiError; - - if (nvr>0 && nullPointer(comp, "fmiSetInteger", "value[]", value)) - return fmiError; - - if (comp->loggingOn) - comp->logger(c, comp->instanceName, fmiOK, "log", "fmiSetInteger: nvr = %d", nvr); - -#ifdef SET_INT32 - SET_VARIABLES(Int32) -#else - return fmiError; // not implemented -#endif -} - -fmiStatus fmiSetBoolean(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiBoolean value[]){ - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiSetBoolean", modelInstantiated|modelInitialized)) - return fmiError; - - if (nvr>0 && nullPointer(comp, "fmiSetBoolean", "vr[]", vr)) - return fmiError; - - if (nvr>0 && nullPointer(comp, "fmiSetBoolean", "value[]", value)) - return fmiError; - - if (comp->loggingOn) - comp->logger(c, comp->instanceName, fmiOK, "log", "fmiSetBoolean: nvr = %d", nvr); - - SET_BOOLEAN_VARIABLES -} - -fmiStatus fmiSetString(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiString value[]){ -// int i; -// ModelInstance* comp = (ModelInstance *)c; -// if (invalidState(comp, "fmiSetString", modelInstantiated|modelInitialized)) -// return fmiError; -// if (nvr>0 && nullPointer(comp, "fmiSetString", "vr[]", vr)) -// return fmiError; -// if (nvr>0 && nullPointer(comp, "fmiSetString", "value[]", value)) -// return fmiError; -// if (comp->loggingOn) -// comp->functions.logger(c, comp->instanceName, fmiOK, "log", "fmiSetString: nvr = %d", nvr); -// for (i=0; is[vr[i]]; -// if (vrOutOfRange(comp, "fmiSetString", vr[i], NUMBER_OF_STRINGS)) -// return fmiError; -// if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", -// "fmiSetString: #s%d# = '%s'", vr[i], value[i]); -// if (value[i] == NULL) { -// if (string) comp->functions.freeMemory(string); -// comp->s[vr[i]] = NULL; -// comp->functions.logger(comp, comp->instanceName, fmiWarning, "warning", -// "fmiSetString: string argument value[%d] = NULL.", i); -// } else { -// if (string==NULL || strlen(string) < strlen(value[i])) { -// if (string) comp->functions.freeMemory(string); -// comp->s[vr[i]] = (char *)comp->functions.allocateMemory(1+strlen(value[i]), sizeof(char)); -// if (!comp->s[vr[i]]) { -// comp->state = modelError; -// comp->functions.logger(NULL, comp->instanceName, fmiError, "error", "fmiSetString: Out of memory."); -// return fmiError; -// } -// } -// strcpy((char *)comp->s[vr[i]], (char *)value[i]); -// } -// } - return fmiOK; -} - -fmiStatus fmiGetReal(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiReal value[]) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiGetReal", not_modelError)) - return fmiError; - - if (nvr > 0 && nullPointer(comp, "fmiGetReal", "vr[]", vr)) - return fmiError; - - if (nvr > 0 && nullPointer(comp, "fmiGetReal", "value[]", value)) - return fmiError; - - GET_VARIABLES(Float64) -} - -fmiStatus fmiGetInteger(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiInteger value[]) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiGetInteger", not_modelError)) - return fmiError; - - if (nvr > 0 && nullPointer(comp, "fmiGetInteger", "vr[]", vr)) - return fmiError; - - if (nvr > 0 && nullPointer(comp, "fmiGetInteger", "value[]", value)) - return fmiError; - - GET_VARIABLES(Int32) -} - -fmiStatus fmiGetBoolean(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiBoolean value[]) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiGetBoolean", not_modelError)) - return fmiError; - - if (nvr>0 && nullPointer(comp, "fmiGetBoolean", "vr[]", vr)) - return fmiError; - - if (nvr>0 && nullPointer(comp, "fmiGetBoolean", "value[]", value)) - return fmiError; - - GET_BOOLEAN_VARIABLES -} - -fmiStatus fmiGetString(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiString value[]) { -// int i; -// ModelInstance* comp = (ModelInstance *)c; -// if (invalidState(comp, "fmiGetString", not_modelError)) -// return fmiError; -// if (nvr>0 && nullPointer(comp, "fmiGetString", "vr[]", vr)) -// return fmiError; -// if (nvr>0 && nullPointer(comp, "fmiGetString", "value[]", value)) -// return fmiError; -// for (i=0; is[vr[i]]; -// if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", -// "fmiGetString: #s%u# = '%s'", vr[i], value[i]); -// } - return fmiOK; -} - -#ifdef FMI_COSIMULATION -// --------------------------------------------------------------------------- -// FMI functions: only for FMI Co-Simulation 1.0 -// --------------------------------------------------------------------------- - -const char* fmiGetTypesPlatform() { - return fmiPlatform; -} - -fmiComponent fmiInstantiateSlave(fmiString instanceName, fmiString GUID, - fmiString fmuLocation, fmiString mimeType, fmiReal timeout, fmiBoolean visible, - fmiBoolean interactive, fmiCallbackFunctions functions, fmiBoolean loggingOn) { - // ignoring arguments: mimeType, timeout, visible, interactive - return instantiateModel("fmiInstantiateSlave", instanceName, GUID, fmuLocation, functions, loggingOn); -} - -fmiStatus fmiInitializeSlave(fmiComponent c, fmiReal tStart, fmiBoolean StopTimeDefined, fmiReal tStop) { - init(c); - return fmiOK; -} - -fmiStatus fmiTerminateSlave(fmiComponent c) { - return terminate("fmiTerminateSlave", c); -} - -fmiStatus fmiResetSlave(fmiComponent c) { - ModelInstance* comp = (ModelInstance *)c; - if (invalidState(comp, "fmiResetSlave", modelInitialized)) - return fmiError; - if (comp->loggingOn) comp->logger(c, comp->instanceName, fmiOK, "log", "fmiResetSlave"); - comp->state = modelInstantiated; - setStartValues(comp); // to be implemented by the includer of this file - return fmiOK; -} - -void fmiFreeSlaveInstance(fmiComponent c) { -// ModelInstance* comp = (ModelInstance *)c; -// if (invalidState(comp, "fmiFreeSlaveInstance", modelTerminated)) -// return; -// freeInstance("fmiFreeSlaveInstance", c); -} - -fmiStatus fmiSetRealInputDerivatives(fmiComponent c, const fmiValueReference vr[], size_t nvr, - const fmiInteger order[], const fmiReal value[]) { - ModelInstance* comp = (ModelInstance *)c; - fmiCallbackLogger log = comp->logger; - if (invalidState(comp, "fmiSetRealInputDerivatives", modelInitialized)) - return fmiError; - if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "fmiSetRealInputDerivatives: nvr= %d", nvr); - log(c, comp->instanceName, fmiError, "warning", "fmiSetRealInputDerivatives: ignoring function call." - " This model cannot interpolate inputs: canInterpolateInputs=\"fmiFalse\""); - return fmiWarning; -} - -fmiStatus fmiGetRealOutputDerivatives(fmiComponent c, const fmiValueReference vr[], size_t nvr, - const fmiInteger order[], fmiReal value[]) { - int i; - ModelInstance* comp = (ModelInstance *)c; - fmiCallbackLogger log = comp->logger; - if (invalidState(comp, "fmiGetRealOutputDerivatives", modelInitialized)) - return fmiError; - if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "fmiGetRealOutputDerivatives: nvr= %d", nvr); - log(c, comp->instanceName, fmiError, "warning", "fmiGetRealOutputDerivatives: ignoring function call." - " This model cannot compute derivatives of outputs: MaxOutputDerivativeOrder=\"0\""); - for (i=0; ilogger; - if (invalidState(comp, "fmiCancelStep", modelInitialized)) - return fmiError; - if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "fmiCancelStep"); - log(c, comp->instanceName, fmiError, "error", - "fmiCancelStep: Can be called when fmiDoStep returned fmiPending." - " This is not the case."); - return fmiError; -} - -fmiStatus fmiDoStep(fmiComponent c, fmiReal currentCommunicationPoint, fmiReal communicationStepSize, fmiBoolean newStep) { - ModelInstance* comp = (ModelInstance *)c; - return doStep(comp, currentCommunicationPoint, currentCommunicationPoint + communicationStepSize); -} - -static fmiStatus getStatus(char* fname, fmiComponent c, const fmiStatusKind s) { -// const char* statusKind[3] = {"fmiDoStepStatus","fmiPendingStatus","fmiLastSuccessfulTime"}; -// ModelInstance* comp = (ModelInstance *)c; -// fmiCallbackLogger log = comp->functions.logger; -// if (invalidState(comp, fname, modelInstantiated|modelInitialized)) -// return fmiError; -// if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "$s: fmiStatusKind = %s", fname, statusKind[s]); -// switch(s) { -// case fmiDoStepStatus: log(c, comp->instanceName, fmiError, "error", -// "%s: Can be called with fmiDoStepStatus when fmiDoStep returned fmiPending." -// " This is not the case.", fname); -// break; -// case fmiPendingStatus: log(c, comp->instanceName, fmiError, "error", -// "%s: Can be called with fmiPendingStatus when fmiDoStep returned fmiPending." -// " This is not the case.", fname); -// break; -// case fmiLastSuccessfulTime: log(c, comp->instanceName, fmiError, "error", -// "%s: Can be called with fmiLastSuccessfulTime when fmiDoStep returned fmiDiscard." -// " This is not the case.", fname); -// break; -// } - return fmiError; -} - -fmiStatus fmiGetStatus(fmiComponent c, const fmiStatusKind s, fmiStatus* value) { - return getStatus("fmiGetStatus", c, s); -} - -fmiStatus fmiGetRealStatus(fmiComponent c, const fmiStatusKind s, fmiReal* value){ - return getStatus("fmiGetRealStatus", c, s); -} - -fmiStatus fmiGetIntegerStatus(fmiComponent c, const fmiStatusKind s, fmiInteger* value){ - return getStatus("fmiGetIntegerStatus", c, s); -} - -fmiStatus fmiGetBooleanStatus(fmiComponent c, const fmiStatusKind s, fmiBoolean* value){ - return getStatus("fmiGetBooleanStatus", c, s); -} - -fmiStatus fmiGetStringStatus(fmiComponent c, const fmiStatusKind s, fmiString* value){ - return getStatus("fmiGetStringStatus", c, s); -} - -#else -// --------------------------------------------------------------------------- -// FMI functions: only for Model Exchange 1.0 -// --------------------------------------------------------------------------- - -const char* fmiGetModelTypesPlatform() { - return fmiModelTypesPlatform; -} - -fmiComponent fmiInstantiateModel(fmiString instanceName, fmiString GUID, fmiCallbackFunctions functions, fmiBoolean loggingOn) { - return instantiateModel("fmiInstantiateModel", instanceName, GUID, NULL, functions, loggingOn); -} - -fmiStatus fmiInitialize(fmiComponent c, fmiBoolean toleranceControlled, fmiReal relativeTolerance, fmiEventInfo* eventInfo) { - return init(c); -} - -fmiStatus fmiSetTime(fmiComponent c, fmiReal time) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiSetTime", modelInstantiated|modelInitialized)) - return fmiError; - - if (comp->loggingOn) comp->logger(c, comp->instanceName, fmiOK, "log", "fmiSetTime: time=%.16g", time); - - comp->time = time; - - return fmiOK; -} - -fmiStatus fmiSetContinuousStates(fmiComponent c, const fmiReal x[], size_t nx) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiSetContinuousStates", modelInitialized)) - return fmiError; - - if (invalidNumber(comp, "fmiSetContinuousStates", "nx", nx, NUMBER_OF_STATES)) - return fmiError; - - if (nullPointer(comp, "fmiSetContinuousStates", "x[]", x)) - return fmiError; - - setContinuousStates(comp, x, nx); - - return fmiOK; -} - -fmiStatus fmiEventUpdate(fmiComponent c, fmiBoolean intermediateResults, fmiEventInfo* eventInfo) { - - ModelInstance* comp = (ModelInstance *)c; - - int timeEvent = 0; - - if (invalidState(comp, "fmiEventUpdate", modelInitialized)) - return fmiError; - - if (nullPointer(comp, "fmiEventUpdate", "eventInfo", eventInfo)) - return fmiError; - - if (comp->loggingOn) comp->logger(c, comp->instanceName, fmiOK, "log", - "fmiEventUpdate: intermediateResults = %d", intermediateResults); - - comp->newDiscreteStatesNeeded = false; - comp->terminateSimulation = false; - comp->nominalsOfContinuousStatesChanged = false; - comp->valuesOfContinuousStatesChanged = false; - - if (comp->nextEventTimeDefined && comp->nextEventTime <= comp->time) { - timeEvent = 1; - } - - eventUpdate(comp); - - // copy internal eventInfo of component to output eventInfo - eventInfo->iterationConverged = fmiTrue; - eventInfo->stateValueReferencesChanged = fmiFalse; - eventInfo->stateValuesChanged = comp->valuesOfContinuousStatesChanged; - eventInfo->terminateSimulation = comp->terminateSimulation; - eventInfo->upcomingTimeEvent = comp->nextEventTimeDefined; - eventInfo->nextEventTime = comp->nextEventTime; - - return fmiOK; -} - -fmiStatus fmiCompletedIntegratorStep(fmiComponent c, fmiBoolean* callEventUpdate) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiCompletedIntegratorStep", modelInitialized)) - return fmiError; - - if (nullPointer(comp, "fmiCompletedIntegratorStep", "callEventUpdate", callEventUpdate)) - return fmiError; - - if (comp->loggingOn) comp->logger(c, comp->instanceName, fmiOK, "log", "fmiCompletedIntegratorStep"); - - return fmiOK; -} - -fmiStatus fmiGetStateValueReferences(fmiComponent c, fmiValueReference vrx[], size_t nx) { -// int i; -// ModelInstance* comp = (ModelInstance *)c; -// if (invalidState(comp, "fmiGetStateValueReferences", not_modelError)) -// return fmiError; -// if (invalidNumber(comp, "fmiGetStateValueReferences", "nx", nx, NUMBER_OF_STATES)) -// return fmiError; -// if (nullPointer(comp, "fmiGetStateValueReferences", "vrx[]", vrx)) -// return fmiError; -//#if NUMBER_OF_STATES>0 -// for (i=0; iloggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", -// "fmiGetStateValueReferences: vrx[%d] = %d", i, vrx[i]); -// } -//#endif - return fmiOK; -} - -fmiStatus fmiGetContinuousStates(fmiComponent c, fmiReal states[], size_t nx){ - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmiGetContinuousStates", not_modelError)) - return fmiError; - - if (invalidNumber(comp, "fmiGetContinuousStates", "nx", nx, NUMBER_OF_STATES)) - return fmiError; - - if (nullPointer(comp, "fmiGetContinuousStates", "states[]", states)) - return fmiError; - - getContinuousStates(comp, states, nx); - - return fmiOK; -} - -fmiStatus fmiGetNominalContinuousStates(fmiComponent c, fmiReal x_nominal[], size_t nx) { -// int i; -// ModelInstance* comp = (ModelInstance *)c; -// if (invalidState(comp, "fmiGetNominalContinuousStates", not_modelError)) -// return fmiError; -// if (invalidNumber(comp, "fmiGetNominalContinuousStates", "nx", nx, NUMBER_OF_STATES)) -// return fmiError; -// if (nullPointer(comp, "fmiGetNominalContinuousStates", "x_nominal[]", x_nominal)) -// return fmiError; -// if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", -// "fmiGetNominalContinuousStates: x_nominal[0..%d] = 1.0", nx-1); -// for (i=0; i +#include +#include +#include + +#include "config.h" +#include "model.h" +#include "slave.h" + + +#ifdef FMI_COSIMULATION +#include "fmiFunctions.h" +#else +#include "fmiModelFunctions.h" +#endif + +#ifndef max +#define max(a,b) ((a)>(b) ? (a) : (b)) +#endif + +#ifndef DT_EVENT_DETECT +#define DT_EVENT_DETECT 1e-10 +#endif + +// --------------------------------------------------------------------------- +// Private helpers used below to implement functions +// --------------------------------------------------------------------------- + +// fname is fmiInitialize or fmiInitializeSlave +static fmiStatus init(fmiComponent c) { + ModelInstance* comp = (ModelInstance *)c; + comp->state = modelInitialized; + calculateValues(comp); + return fmiOK; +} + +// fname is fmiTerminate or fmiTerminateSlave +static fmiStatus terminate(char* fname, fmiComponent c) { + ModelInstance* comp = (ModelInstance *)c; + if (invalidState(comp, fname, modelInitialized)) + return fmiError; + comp->state = modelTerminated; + return fmiOK; +} + +// --------------------------------------------------------------------------- +// FMI functions: class methods not depending of a specific model instance +// --------------------------------------------------------------------------- + +const char* fmiGetVersion() { + return fmiVersion; +} + +// --------------------------------------------------------------------------- +// FMI functions: for FMI Model Exchange 1.0 and for FMI Co-Simulation 1.0 +// logging control, setters and getters for Real, Integer, Boolean, String +// --------------------------------------------------------------------------- + +fmiStatus fmiSetDebugLogging(fmiComponent c, fmiBoolean loggingOn) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiSetDebugLogging", not_modelError)) + return fmiError; + + return setDebugLogging(comp, loggingOn, 0, NULL); +} + +fmiStatus fmiSetReal(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiReal value[]) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiSetReal", modelInstantiated|modelInitialized)) + return fmiError; + + if (nvr>0 && nullPointer(comp, "fmiSetReal", "vr[]", vr)) + return fmiError; + + if (nvr>0 && nullPointer(comp, "fmiSetReal", "value[]", value)) + return fmiError; + +#ifdef SET_FLOAT64 + SET_VARIABLES(Float64) +#else + return fmiError; // not implemented +#endif +} + +fmiStatus fmiSetInteger(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiInteger value[]) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiSetInteger", modelInstantiated|modelInitialized)) + return fmiError; + + if (nvr>0 && nullPointer(comp, "fmiSetInteger", "vr[]", vr)) + return fmiError; + + if (nvr>0 && nullPointer(comp, "fmiSetInteger", "value[]", value)) + return fmiError; + +#ifdef SET_INT32 + SET_VARIABLES(Int32) +#else + return fmiError; // not implemented +#endif +} + +fmiStatus fmiSetBoolean(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiBoolean value[]){ + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiSetBoolean", modelInstantiated|modelInitialized)) + return fmiError; + + if (nvr>0 && nullPointer(comp, "fmiSetBoolean", "vr[]", vr)) + return fmiError; + + if (nvr>0 && nullPointer(comp, "fmiSetBoolean", "value[]", value)) + return fmiError; + + SET_BOOLEAN_VARIABLES +} + +fmiStatus fmiSetString(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiString value[]){ +// int i; +// ModelInstance* comp = (ModelInstance *)c; +// if (invalidState(comp, "fmiSetString", modelInstantiated|modelInitialized)) +// return fmiError; +// if (nvr>0 && nullPointer(comp, "fmiSetString", "vr[]", vr)) +// return fmiError; +// if (nvr>0 && nullPointer(comp, "fmiSetString", "value[]", value)) +// return fmiError; +// if (comp->loggingOn) +// comp->functions.logger(c, comp->instanceName, fmiOK, "log", "fmiSetString: nvr = %d", nvr); +// for (i=0; is[vr[i]]; +// if (vrOutOfRange(comp, "fmiSetString", vr[i], NUMBER_OF_STRINGS)) +// return fmiError; +// if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", +// "fmiSetString: #s%d# = '%s'", vr[i], value[i]); +// if (value[i] == NULL) { +// if (string) comp->functions.freeMemory(string); +// comp->s[vr[i]] = NULL; +// comp->functions.logger(comp, comp->instanceName, fmiWarning, "warning", +// "fmiSetString: string argument value[%d] = NULL.", i); +// } else { +// if (string==NULL || strlen(string) < strlen(value[i])) { +// if (string) comp->functions.freeMemory(string); +// comp->s[vr[i]] = (char *)comp->functions.allocateMemory(1+strlen(value[i]), sizeof(char)); +// if (!comp->s[vr[i]]) { +// comp->state = modelError; +// comp->functions.logger(NULL, comp->instanceName, fmiError, "error", "fmiSetString: Out of memory."); +// return fmiError; +// } +// } +// strcpy((char *)comp->s[vr[i]], (char *)value[i]); +// } +// } + return fmiOK; +} + +fmiStatus fmiGetReal(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiReal value[]) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiGetReal", not_modelError)) + return fmiError; + + if (nvr > 0 && nullPointer(comp, "fmiGetReal", "vr[]", vr)) + return fmiError; + + if (nvr > 0 && nullPointer(comp, "fmiGetReal", "value[]", value)) + return fmiError; + + GET_VARIABLES(Float64) +} + +fmiStatus fmiGetInteger(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiInteger value[]) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiGetInteger", not_modelError)) + return fmiError; + + if (nvr > 0 && nullPointer(comp, "fmiGetInteger", "vr[]", vr)) + return fmiError; + + if (nvr > 0 && nullPointer(comp, "fmiGetInteger", "value[]", value)) + return fmiError; + + GET_VARIABLES(Int32) +} + +fmiStatus fmiGetBoolean(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiBoolean value[]) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiGetBoolean", not_modelError)) + return fmiError; + + if (nvr>0 && nullPointer(comp, "fmiGetBoolean", "vr[]", vr)) + return fmiError; + + if (nvr>0 && nullPointer(comp, "fmiGetBoolean", "value[]", value)) + return fmiError; + + GET_BOOLEAN_VARIABLES +} + +fmiStatus fmiGetString(fmiComponent c, const fmiValueReference vr[], size_t nvr, fmiString value[]) { +// int i; +// ModelInstance* comp = (ModelInstance *)c; +// if (invalidState(comp, "fmiGetString", not_modelError)) +// return fmiError; +// if (nvr>0 && nullPointer(comp, "fmiGetString", "vr[]", vr)) +// return fmiError; +// if (nvr>0 && nullPointer(comp, "fmiGetString", "value[]", value)) +// return fmiError; +// for (i=0; is[vr[i]]; +// if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", +// "fmiGetString: #s%u# = '%s'", vr[i], value[i]); +// } + return fmiOK; +} + +#ifdef FMI_COSIMULATION +// --------------------------------------------------------------------------- +// FMI functions: only for FMI Co-Simulation 1.0 +// --------------------------------------------------------------------------- + +const char* fmiGetTypesPlatform() { + return fmiPlatform; +} + +fmiComponent fmiInstantiateSlave(fmiString instanceName, fmiString GUID, + fmiString fmuLocation, fmiString mimeType, fmiReal timeout, fmiBoolean visible, + fmiBoolean interactive, fmiCallbackFunctions functions, fmiBoolean loggingOn) { + + // ignoring arguments: mimeType, timeout, visible, interactive + return createModelInstance( + (loggerType)functions.logger, + (allocateMemoryType)functions.allocateMemory, + (freeMemoryType)functions.freeMemory, + NULL, + instanceName, + GUID, + fmuLocation, + loggingOn, + CoSimulation); +} + +fmiStatus fmiInitializeSlave(fmiComponent c, fmiReal tStart, fmiBoolean StopTimeDefined, fmiReal tStop) { + return init(c); +} + +fmiStatus fmiTerminateSlave(fmiComponent c) { + return terminate("fmiTerminateSlave", c); +} + +fmiStatus fmiResetSlave(fmiComponent c) { + ModelInstance* comp = (ModelInstance *)c; + if (invalidState(comp, "fmiResetSlave", modelInitialized)) + return fmiError; + comp->state = modelInstantiated; + setStartValues(comp); // to be implemented by the includer of this file + return fmiOK; +} + +void fmiFreeSlaveInstance(fmiComponent c) { + ModelInstance *comp = (ModelInstance *)c; + freeModelInstance(comp); +} + +fmiStatus fmiSetRealInputDerivatives(fmiComponent c, const fmiValueReference vr[], size_t nvr, + const fmiInteger order[], const fmiReal value[]) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiSetRealInputDerivatives", modelInitialized)) + return fmiError; + + logError(comp, "fmiSetRealInputDerivatives: This model cannot interpolate inputs: canInterpolateInputs=\"fmiFalse\""); + + return fmiError; +} + +fmiStatus fmiGetRealOutputDerivatives(fmiComponent c, const fmiValueReference vr[], size_t nvr, const fmiInteger order[], fmiReal value[]) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiGetRealOutputDerivatives", modelInitialized)) + return fmiError; + + logError(comp, "fmiGetRealOutputDerivatives: This model cannot compute derivatives of outputs: MaxOutputDerivativeOrder=\"0\""); + + return fmiError; +} + +fmiStatus fmiCancelStep(fmiComponent c) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiCancelStep", modelInitialized)) + return fmiError; + + logError(comp, "fmiCancelStep: Can be called when fmiDoStep returned fmiPending." + " This is not the case."); + + return fmiError; +} + +fmiStatus fmiDoStep(fmiComponent c, fmiReal currentCommunicationPoint, fmiReal communicationStepSize, fmiBoolean newStep) { + ModelInstance* comp = (ModelInstance *)c; + return doStep(comp, currentCommunicationPoint, currentCommunicationPoint + communicationStepSize); +} + +static fmiStatus getStatus(char* fname, fmiComponent c, const fmiStatusKind s) { +// const char* statusKind[3] = {"fmiDoStepStatus","fmiPendingStatus","fmiLastSuccessfulTime"}; +// ModelInstance* comp = (ModelInstance *)c; +// fmiCallbackLogger log = comp->functions.logger; +// if (invalidState(comp, fname, modelInstantiated|modelInitialized)) +// return fmiError; +// if (comp->loggingOn) log(c, comp->instanceName, fmiOK, "log", "$s: fmiStatusKind = %s", fname, statusKind[s]); +// switch(s) { +// case fmiDoStepStatus: log(c, comp->instanceName, fmiError, "error", +// "%s: Can be called with fmiDoStepStatus when fmiDoStep returned fmiPending." +// " This is not the case.", fname); +// break; +// case fmiPendingStatus: log(c, comp->instanceName, fmiError, "error", +// "%s: Can be called with fmiPendingStatus when fmiDoStep returned fmiPending." +// " This is not the case.", fname); +// break; +// case fmiLastSuccessfulTime: log(c, comp->instanceName, fmiError, "error", +// "%s: Can be called with fmiLastSuccessfulTime when fmiDoStep returned fmiDiscard." +// " This is not the case.", fname); +// break; +// } + return fmiError; +} + +fmiStatus fmiGetStatus(fmiComponent c, const fmiStatusKind s, fmiStatus* value) { + return getStatus("fmiGetStatus", c, s); +} + +fmiStatus fmiGetRealStatus(fmiComponent c, const fmiStatusKind s, fmiReal* value){ + return getStatus("fmiGetRealStatus", c, s); +} + +fmiStatus fmiGetIntegerStatus(fmiComponent c, const fmiStatusKind s, fmiInteger* value){ + return getStatus("fmiGetIntegerStatus", c, s); +} + +fmiStatus fmiGetBooleanStatus(fmiComponent c, const fmiStatusKind s, fmiBoolean* value){ + return getStatus("fmiGetBooleanStatus", c, s); +} + +fmiStatus fmiGetStringStatus(fmiComponent c, const fmiStatusKind s, fmiString* value){ + return getStatus("fmiGetStringStatus", c, s); +} + +#else +// --------------------------------------------------------------------------- +// FMI functions: only for Model Exchange 1.0 +// --------------------------------------------------------------------------- + +const char* fmiGetModelTypesPlatform() { + return fmiModelTypesPlatform; +} + +fmiComponent fmiInstantiateModel(fmiString instanceName, fmiString GUID, fmiCallbackFunctions functions, fmiBoolean loggingOn) { + return createModelInstance( + (loggerType)functions.logger, + (allocateMemoryType)functions.allocateMemory, + (freeMemoryType)functions.freeMemory, + NULL, + instanceName, + GUID, + NULL, + loggingOn, + ModelExchange); +} + +fmiStatus fmiInitialize(fmiComponent c, fmiBoolean toleranceControlled, fmiReal relativeTolerance, fmiEventInfo* eventInfo) { + ModelInstance *comp = (ModelInstance *)c; + + eventInfo->iterationConverged = fmiTrue; + eventInfo->stateValueReferencesChanged = fmiFalse; + eventInfo->stateValuesChanged = fmiFalse; + eventInfo->terminateSimulation = fmiFalse; + eventInfo->upcomingTimeEvent = comp->nextEventTimeDefined; + eventInfo->nextEventTime = comp->nextEventTime; + + return init(c); +} + +fmiStatus fmiSetTime(fmiComponent c, fmiReal time) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiSetTime", modelInstantiated|modelInitialized)) + return fmiError; + + comp->time = time; + + return fmiOK; +} + +fmiStatus fmiSetContinuousStates(fmiComponent c, const fmiReal x[], size_t nx) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiSetContinuousStates", modelInitialized)) + return fmiError; + + if (invalidNumber(comp, "fmiSetContinuousStates", "nx", nx, NUMBER_OF_STATES)) + return fmiError; + + if (nullPointer(comp, "fmiSetContinuousStates", "x[]", x)) + return fmiError; + + setContinuousStates(comp, x, nx); + + return fmiOK; +} + +fmiStatus fmiEventUpdate(fmiComponent c, fmiBoolean intermediateResults, fmiEventInfo* eventInfo) { + + ModelInstance* comp = (ModelInstance *)c; + + int timeEvent = 0; + + if (invalidState(comp, "fmiEventUpdate", modelInitialized)) + return fmiError; + + if (nullPointer(comp, "fmiEventUpdate", "eventInfo", eventInfo)) + return fmiError; + + comp->newDiscreteStatesNeeded = false; + comp->terminateSimulation = false; + comp->nominalsOfContinuousStatesChanged = false; + comp->valuesOfContinuousStatesChanged = false; + + if (comp->nextEventTimeDefined && comp->nextEventTime <= comp->time) { + timeEvent = 1; + } + + eventUpdate(comp); + + // copy internal eventInfo of component to output eventInfo + eventInfo->iterationConverged = fmiTrue; + eventInfo->stateValueReferencesChanged = fmiFalse; + eventInfo->stateValuesChanged = comp->valuesOfContinuousStatesChanged; + eventInfo->terminateSimulation = comp->terminateSimulation; + eventInfo->upcomingTimeEvent = comp->nextEventTimeDefined; + eventInfo->nextEventTime = comp->nextEventTime; + + return fmiOK; +} + +fmiStatus fmiCompletedIntegratorStep(fmiComponent c, fmiBoolean* callEventUpdate) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiCompletedIntegratorStep", modelInitialized)) + return fmiError; + + if (nullPointer(comp, "fmiCompletedIntegratorStep", "callEventUpdate", callEventUpdate)) + return fmiError; + + return fmiOK; +} + +fmiStatus fmiGetStateValueReferences(fmiComponent c, fmiValueReference vrx[], size_t nx) { +// int i; +// ModelInstance* comp = (ModelInstance *)c; +// if (invalidState(comp, "fmiGetStateValueReferences", not_modelError)) +// return fmiError; +// if (invalidNumber(comp, "fmiGetStateValueReferences", "nx", nx, NUMBER_OF_STATES)) +// return fmiError; +// if (nullPointer(comp, "fmiGetStateValueReferences", "vrx[]", vrx)) +// return fmiError; +//#if NUMBER_OF_STATES>0 +// for (i=0; iloggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", +// "fmiGetStateValueReferences: vrx[%d] = %d", i, vrx[i]); +// } +//#endif + return fmiOK; +} + +fmiStatus fmiGetContinuousStates(fmiComponent c, fmiReal states[], size_t nx){ + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmiGetContinuousStates", not_modelError)) + return fmiError; + + if (invalidNumber(comp, "fmiGetContinuousStates", "nx", nx, NUMBER_OF_STATES)) + return fmiError; + + if (nullPointer(comp, "fmiGetContinuousStates", "states[]", states)) + return fmiError; + + getContinuousStates(comp, states, nx); + + return fmiOK; +} + +fmiStatus fmiGetNominalContinuousStates(fmiComponent c, fmiReal x_nominal[], size_t nx) { +// int i; +// ModelInstance* comp = (ModelInstance *)c; +// if (invalidState(comp, "fmiGetNominalContinuousStates", not_modelError)) +// return fmiError; +// if (invalidNumber(comp, "fmiGetNominalContinuousStates", "nx", nx, NUMBER_OF_STATES)) +// return fmiError; +// if (nullPointer(comp, "fmiGetNominalContinuousStates", "x_nominal[]", x_nominal)) +// return fmiError; +// if (comp->loggingOn) comp->functions.logger(c, comp->instanceName, fmiOK, "log", +// "fmiGetNominalContinuousStates: x_nominal[0..%d] = 1.0", nx-1); +// for (i=0; i -#include -#include -#include - -#include "config.h" -#include "model.h" -#include "solver.h" -#include "slave.h" - - -// C-code FMUs have functions names prefixed with MODEL_IDENTIFIER_. -// Define DISABLE_PREFIX to build a binary FMU. -#ifndef DISABLE_PREFIX -#define pasteA(a,b) a ## b -#define pasteB(a,b) pasteA(a,b) -#define FMI2_FUNCTION_PREFIX pasteB(MODEL_IDENTIFIER, _) -#endif -#include "fmi2Functions.h" - - -// macro to be used to log messages. The macro check if current -// log category is valid and, if true, call the logger provided by simulator. -#define FILTERED_LOG(instance, status, categoryIndex, message, ...) if (status == fmi2Error || status == fmi2Fatal || isCategoryLogged(instance, categoryIndex)) \ - instance->logger(instance->componentEnvironment, instance->instanceName, status, \ - logCategoriesNames[categoryIndex], message, ##__VA_ARGS__); - -static const char *logCategoriesNames[] = {"logAll", "logError", "logFmiCall", "logEvent"}; - -#ifndef max -#define max(a,b) ((a)>(b) ? (a) : (b)) -#endif - -#ifndef DT_EVENT_DETECT -#define DT_EVENT_DETECT 1e-10 -#endif - -// --------------------------------------------------------------------------- -// Function calls allowed state masks for both Model-exchange and Co-simulation -// --------------------------------------------------------------------------- -#define MASK_fmi2GetTypesPlatform (modelStartAndEnd | modelInstantiated | modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepInProgress | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi2GetVersion MASK_fmi2GetTypesPlatform -#define MASK_fmi2SetDebugLogging (modelInstantiated | modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepInProgress | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi2Instantiate (modelStartAndEnd) -#define MASK_fmi2FreeInstance (modelInstantiated | modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi2SetupExperiment modelInstantiated -#define MASK_fmi2EnterInitializationMode modelInstantiated -#define MASK_fmi2ExitInitializationMode modelInitializationMode -#define MASK_fmi2Terminate (modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepFailed) -#define MASK_fmi2Reset MASK_fmi2FreeInstance -#define MASK_fmi2GetReal (modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi2GetInteger MASK_fmi2GetReal -#define MASK_fmi2GetBoolean MASK_fmi2GetReal -#define MASK_fmi2GetString MASK_fmi2GetReal -#define MASK_fmi2SetReal (modelInstantiated | modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete) -#define MASK_fmi2SetInteger (modelInstantiated | modelInitializationMode \ -| modelEventMode \ -| modelStepComplete) -#define MASK_fmi2SetBoolean MASK_fmi2SetInteger -#define MASK_fmi2SetString MASK_fmi2SetInteger -#define MASK_fmi2GetFMUstate MASK_fmi2FreeInstance -#define MASK_fmi2SetFMUstate MASK_fmi2FreeInstance -#define MASK_fmi2FreeFMUstate MASK_fmi2FreeInstance -#define MASK_fmi2SerializedFMUstateSize MASK_fmi2FreeInstance -#define MASK_fmi2SerializeFMUstate MASK_fmi2FreeInstance -#define MASK_fmi2DeSerializeFMUstate MASK_fmi2FreeInstance -#define MASK_fmi2GetDirectionalDerivative (modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) - -// --------------------------------------------------------------------------- -// Function calls allowed state masks for Model-exchange -// --------------------------------------------------------------------------- -#define MASK_fmi2EnterEventMode (modelEventMode | modelContinuousTimeMode) -#define MASK_fmi2NewDiscreteStates modelEventMode -#define MASK_fmi2EnterContinuousTimeMode modelEventMode -#define MASK_fmi2CompletedIntegratorStep modelContinuousTimeMode -#define MASK_fmi2SetTime (modelEventMode | modelContinuousTimeMode) -#define MASK_fmi2SetContinuousStates modelContinuousTimeMode -#define MASK_fmi2GetEventIndicators (modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelTerminated | modelError) -#define MASK_fmi2GetContinuousStates MASK_fmi2GetEventIndicators -#define MASK_fmi2GetDerivatives (modelEventMode | modelContinuousTimeMode \ -| modelTerminated | modelError) -#define MASK_fmi2GetNominalsOfContinuousStates ( modelInstantiated \ -| modelEventMode | modelContinuousTimeMode \ -| modelTerminated | modelError) - -// --------------------------------------------------------------------------- -// Function calls allowed state masks for Co-simulation -// --------------------------------------------------------------------------- -#define MASK_fmi2SetRealInputDerivatives (modelInstantiated | modelInitializationMode \ -| modelStepComplete) -#define MASK_fmi2GetRealOutputDerivatives (modelStepComplete | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi2DoStep modelStepComplete -#define MASK_fmi2CancelStep modelStepInProgress -#define MASK_fmi2GetStatus (modelStepComplete | modelStepInProgress | modelStepFailed \ -| modelTerminated) -#define MASK_fmi2GetRealStatus MASK_fmi2GetStatus -#define MASK_fmi2GetIntegerStatus MASK_fmi2GetStatus -#define MASK_fmi2GetBooleanStatus MASK_fmi2GetStatus -#define MASK_fmi2GetStringStatus MASK_fmi2GetStatus - -// --------------------------------------------------------------------------- -// Private helpers used below to validate function arguments -// --------------------------------------------------------------------------- - -void logError(ModelInstance *comp, const char *message, ...) { - - va_list args; - size_t len = 0; - char *buf = ""; - - va_start(args, message); - len = vsnprintf(buf, len, message, args); - va_end(args); - - buf = malloc(len + 1); - - va_start(args, message); - len = vsnprintf(buf, len + 1, message, args); - va_end(args); - - comp->logger(comp->componentEnvironment, comp->instanceName, fmi2Error, "logError", buf); - - free(buf); -} - -void *allocateMemory(ModelInstance *comp, size_t size) { - return comp->allocateMemory(size, 1); -} - -void freeMemory(ModelInstance *comp, void *obj) { - comp->freeMemory(obj); -} - -const char *duplicateString(ModelInstance *comp, const char *str1) { - size_t len = strlen(str1); - char *str2 = allocateMemory(comp, len + 1); - strncpy(str2, str1, len + 1); - return str2; -} - -fmi2Boolean isCategoryLogged(ModelInstance *comp, int categoryIndex); - -static bool invalidNumber(ModelInstance *comp, const char *f, const char *arg, int n, int nExpected) { - if (n != nExpected) { - comp->state = modelError; - FILTERED_LOG(comp, fmi2Error, LOG_ERROR, "%s: Invalid argument %s = %d. Expected %d.", f, arg, n, nExpected) - return fmi2True; - } - return fmi2False; -} - -static fmi2Boolean invalidState(ModelInstance *comp, const char *f, int statesExpected) { - if (!comp) - return fmi2True; - if (!(comp->state & statesExpected)) { - comp->state = modelError; - FILTERED_LOG(comp, fmi2Error, LOG_ERROR, "%s: Illegal call sequence.", f) - return fmi2True; - } - return fmi2False; -} - -static fmi2Boolean nullPointer(ModelInstance* comp, const char *f, const char *arg, const void *p) { - if (!p) { - comp->state = modelError; - FILTERED_LOG(comp, fmi2Error, LOG_ERROR, "%s: Invalid argument %s = NULL.", f, arg) - return fmi2True; - } - return fmi2False; -} - -static fmi2Status unsupportedFunction(fmi2Component c, const char *fName, int statesExpected) { - ModelInstance *comp = (ModelInstance *)c; - //fmi2CallbackLogger log = comp->functions->logger; - if (invalidState(comp, fName, statesExpected)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, fName); - FILTERED_LOG(comp, fmi2Error, LOG_ERROR, "%s: Function not implemented.", fName) - return fmi2Error; -} - -// --------------------------------------------------------------------------- -// Private helpers logger -// --------------------------------------------------------------------------- - -// return fmi2True if logging category is on. Else return fmi2False. -fmi2Boolean isCategoryLogged(ModelInstance *comp, int categoryIndex) { - if (categoryIndex < NUMBER_OF_CATEGORIES - && (comp->logCategories[categoryIndex] || comp->logCategories[LOG_ALL])) { - return fmi2True; - } - return fmi2False; -} - -// --------------------------------------------------------------------------- -// FMI functions -// --------------------------------------------------------------------------- -fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2String fmuGUID, - fmi2String fmuResourceLocation, const fmi2CallbackFunctions *functions, - fmi2Boolean visible, fmi2Boolean loggingOn) { - // ignoring arguments: fmuResourceLocation, visible - ModelInstance *comp; - if (!functions->logger) { - return NULL; - } - - if (!functions->allocateMemory || !functions->freeMemory) { - functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error", - "fmi2Instantiate: Missing callback function."); - return NULL; - } - if (!instanceName || strlen(instanceName) == 0) { - functions->logger(functions->componentEnvironment, "?", fmi2Error, "error", - "fmi2Instantiate: Missing instance name."); - return NULL; - } - if (!fmuGUID || strlen(fmuGUID) == 0) { - functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error", - "fmi2Instantiate: Missing GUID."); - return NULL; - } - if (strcmp(fmuGUID, MODEL_GUID)) { - functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error", - "fmi2Instantiate: Wrong GUID %s. Expected %s.", fmuGUID, MODEL_GUID); - return NULL; - } - comp = (ModelInstance *)functions->allocateMemory(1, sizeof(ModelInstance)); - if (comp) { - int i; - comp->instanceName = (char *)functions->allocateMemory(1 + strlen(instanceName), sizeof(char)); - comp->GUID = (char *)functions->allocateMemory(1 + strlen(fmuGUID), sizeof(char)); - comp->resourceLocation = (char *)functions->allocateMemory(1 + strlen(fmuResourceLocation), sizeof(char)); - - comp->modelData = (ModelData *)functions->allocateMemory(1, sizeof(ModelData)); - - // set all categories to on or off. fmi2SetDebugLogging should be called to choose specific categories. - for (i = 0; i < NUMBER_OF_CATEGORIES; i++) { - comp->logCategories[i] = loggingOn; - } - } - if (!comp || !comp->modelData || !comp->instanceName || !comp->GUID) { - - functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error", - "fmi2Instantiate: Out of memory."); - return NULL; - } - comp->time = 0; // overwrite in fmi2SetupExperiment, fmi2SetTime - strcpy((char *)comp->instanceName, (char *)instanceName); - comp->type = fmuType; - strcpy((char *)comp->GUID, (char *)fmuGUID); - strcpy((char *)comp->resourceLocation, (char *)fmuResourceLocation); - comp->logger = functions->logger; - comp->allocateMemory = functions->allocateMemory; - comp->freeMemory = functions->freeMemory; - comp->stepFinished = functions->stepFinished; - comp->componentEnvironment = functions->componentEnvironment; - comp->loggingOn = loggingOn; - comp->state = modelInstantiated; - comp->isNewEventIteration = fmi2False; - - comp->newDiscreteStatesNeeded = fmi2False; - comp->terminateSimulation = fmi2False; - comp->nominalsOfContinuousStatesChanged = fmi2False; - comp->valuesOfContinuousStatesChanged = fmi2False; - comp->nextEventTimeDefined = fmi2False; - comp->nextEventTime = 0; - - setStartValues(comp); // to be implemented by the includer of this file - comp->isDirtyValues = true; // because we just called setStartValues - - comp->solverData = solver_create(comp); - - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2Instantiate: GUID=%s", fmuGUID) - - return comp; -} - -fmi2Status fmi2SetupExperiment(fmi2Component c, fmi2Boolean toleranceDefined, fmi2Real tolerance, - fmi2Real startTime, fmi2Boolean stopTimeDefined, fmi2Real stopTime) { - - // ignore arguments: stopTimeDefined, stopTime - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2SetupExperiment", MASK_fmi2SetupExperiment)) - return fmi2Error; - - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetupExperiment: toleranceDefined=%d tolerance=%g", - toleranceDefined, tolerance) - - comp->time = startTime; - - return fmi2OK; -} - -fmi2Status fmi2EnterInitializationMode(fmi2Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2EnterInitializationMode", MASK_fmi2EnterInitializationMode)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2EnterInitializationMode") - - comp->state = modelInitializationMode; - return fmi2OK; -} - -fmi2Status fmi2ExitInitializationMode(fmi2Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2ExitInitializationMode", MASK_fmi2ExitInitializationMode)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2ExitInitializationMode") - - // if values were set and no fmi2GetXXX triggered update before, - // ensure calculated values are updated now - if (comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - if (comp->type == ModelExchange) { - comp->state = modelEventMode; - comp->isNewEventIteration = false; - } - else comp->state = modelStepComplete; - return fmi2OK; -} - -fmi2Status fmi2Terminate(fmi2Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2Terminate", MASK_fmi2Terminate)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2Terminate") - - comp->state = modelTerminated; - return fmi2OK; -} - -fmi2Status fmi2Reset(fmi2Component c) { - ModelInstance* comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2Reset", MASK_fmi2Reset)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2Reset") - - comp->state = modelInstantiated; - setStartValues(comp); // to be implemented by the includer of this file - comp->isDirtyValues = true; // because we just called setStartValues - return fmi2OK; -} - -void fmi2FreeInstance(fmi2Component c) { - - ModelInstance *comp = (ModelInstance *)c; - - if (!comp) return; - - if (invalidState(comp, "fmi2FreeInstance", MASK_fmi2FreeInstance)) - return; - - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2FreeInstance") - - if (comp->instanceName) comp->freeMemory((void *)comp->instanceName); - if (comp->GUID) comp->freeMemory((void *)comp->GUID); - comp->freeMemory(comp); -} - -// --------------------------------------------------------------------------- -// FMI functions: class methods not depending of a specific model instance -// --------------------------------------------------------------------------- - -const char* fmi2GetVersion() { - return fmi2Version; -} - -const char* fmi2GetTypesPlatform() { - return fmi2TypesPlatform; -} - -// --------------------------------------------------------------------------- -// FMI functions: logging control, setters and getters for Real, Integer, -// Boolean, String -// --------------------------------------------------------------------------- - -fmi2Status fmi2SetDebugLogging(fmi2Component c, fmi2Boolean loggingOn, size_t nCategories, const fmi2String categories[]) { - int i, j; - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2SetDebugLogging", MASK_fmi2SetDebugLogging)) - return fmi2Error; - comp->loggingOn = loggingOn; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetDebugLogging") - - // reset all categories - for (j = 0; j < NUMBER_OF_CATEGORIES; j++) { - comp->logCategories[j] = fmi2False; - } - - if (nCategories == 0) { - // no category specified, set all categories to have loggingOn value - for (j = 0; j < NUMBER_OF_CATEGORIES; j++) { - comp->logCategories[j] = loggingOn; - } - } else { - // set specific categories on - for (i = 0; i < nCategories; i++) { - fmi2Boolean categoryFound = fmi2False; - for (j = 0; j < NUMBER_OF_CATEGORIES; j++) { - if (strcmp(logCategoriesNames[j], categories[i]) == 0) { - comp->logCategories[j] = loggingOn; - categoryFound = fmi2True; - break; - } - } - if (!categoryFound) { - comp->logger(comp->componentEnvironment, comp->instanceName, fmi2Warning, - logCategoriesNames[LOG_ERROR], - "logging category '%s' is not supported by model", categories[i]); - } - } - } - return fmi2OK; -} - -fmi2Status fmi2GetReal (fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2Real value[]) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2GetReal", MASK_fmi2GetReal)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2GetReal", "vr[]", vr)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2GetReal", "value[]", value)) - return fmi2Error; - - if (nvr > 0 && comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - GET_VARIABLES(Float64) -} - -fmi2Status fmi2GetInteger(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2Integer value[]) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2GetInteger", MASK_fmi2GetInteger)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2GetInteger", "vr[]", vr)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2GetInteger", "value[]", value)) - return fmi2Error; - - if (nvr > 0 && comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - GET_VARIABLES(Int32) -} - -fmi2Status fmi2GetBoolean(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2Boolean value[]) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2GetBoolean", MASK_fmi2GetBoolean)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2GetBoolean", "vr[]", vr)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2GetBoolean", "value[]", value)) - return fmi2Error; - - if (nvr > 0 && comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - GET_BOOLEAN_VARIABLES -} - -fmi2Status fmi2GetString (fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2String value[]) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2GetString", MASK_fmi2GetString)) - return fmi2Error; - - if (nvr>0 && nullPointer(comp, "fmi2GetString", "vr[]", vr)) - return fmi2Error; - - if (nvr>0 && nullPointer(comp, "fmi2GetString", "value[]", value)) - return fmi2Error; - - if (nvr > 0 && comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - GET_VARIABLES(String) -} - -fmi2Status fmi2SetReal (fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Real value[]) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2SetReal", MASK_fmi2SetReal)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2SetReal", "vr[]", vr)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2SetReal", "value[]", value)) - return fmi2Error; - - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetReal: nvr = %d", nvr) - - SET_VARIABLES(Float64) -} - -fmi2Status fmi2SetInteger(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Integer value[]) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2SetInteger", MASK_fmi2SetInteger)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2SetInteger", "vr[]", vr)) - return fmi2Error; - - if (nvr > 0 && nullPointer(comp, "fmi2SetInteger", "value[]", value)) - return fmi2Error; - - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetInteger: nvr = %d", nvr) - - SET_VARIABLES(Int32) -} - -fmi2Status fmi2SetBoolean(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Boolean value[]) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2SetBoolean", MASK_fmi2SetBoolean)) - return fmi2Error; - - if (nvr>0 && nullPointer(comp, "fmi2SetBoolean", "vr[]", vr)) - return fmi2Error; - - if (nvr>0 && nullPointer(comp, "fmi2SetBoolean", "value[]", value)) - return fmi2Error; - - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetBoolean: nvr = %d", nvr) - - SET_BOOLEAN_VARIABLES -} - -fmi2Status fmi2SetString (fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2String value[]) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2SetString", MASK_fmi2SetString)) - return fmi2Error; - - if (nvr>0 && nullPointer(comp, "fmi2SetString", "vr[]", vr)) - return fmi2Error; - - if (nvr>0 && nullPointer(comp, "fmi2SetString", "value[]", value)) - return fmi2Error; - - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetString: nvr = %d", nvr) - - SET_VARIABLES(String) -} - -fmi2Status fmi2GetFMUstate (fmi2Component c, fmi2FMUstate* FMUstate) { - ModelInstance *comp = (ModelInstance *)c; - ModelData *modelData = comp->allocateMemory(1, sizeof(ModelData)); - memcpy(modelData, comp->modelData, sizeof(ModelData)); - *FMUstate = modelData; - return fmi2OK; -} - -fmi2Status fmi2SetFMUstate (fmi2Component c, fmi2FMUstate FMUstate) { - ModelInstance *comp = (ModelInstance *)c; - ModelData *modelData = FMUstate; - memcpy(comp->modelData, modelData, sizeof(ModelData)); - return fmi2OK; -} - -fmi2Status fmi2FreeFMUstate(fmi2Component c, fmi2FMUstate* FMUstate) { - ModelInstance *comp = (ModelInstance *)c; - ModelData *modelData = *FMUstate; - comp->freeMemory(modelData); - *FMUstate = NULL; - return fmi2OK; -} - -fmi2Status fmi2SerializedFMUstateSize(fmi2Component c, fmi2FMUstate FMUstate, size_t *size) { - *size = sizeof(ModelData); - return fmi2OK; -} - -fmi2Status fmi2SerializeFMUstate (fmi2Component c, fmi2FMUstate FMUstate, fmi2Byte serializedState[], size_t size) { - ModelInstance *comp = (ModelInstance *)c; - // TODO: check size - memcpy(serializedState, comp->modelData, sizeof(ModelData)); - return fmi2OK; -} - -fmi2Status fmi2DeSerializeFMUstate (fmi2Component c, const fmi2Byte serializedState[], size_t size, fmi2FMUstate* FMUstate) { - - ModelInstance *comp = (ModelInstance *)c; - - if (*FMUstate == NULL) { - *FMUstate = comp->allocateMemory(1, sizeof(ModelData)); - } - - // TODO: check size - - memcpy(*FMUstate, serializedState, sizeof(ModelData)); - - return fmi2OK; -} - -fmi2Status fmi2GetDirectionalDerivative(fmi2Component c, const fmi2ValueReference vUnknown_ref[], size_t nUnknown, - const fmi2ValueReference vKnown_ref[] , size_t nKnown, - const fmi2Real dvKnown[], fmi2Real dvUnknown[]) { - return unsupportedFunction(c, "fmi2GetDirectionalDerivative", MASK_fmi2GetDirectionalDerivative); -} - -// --------------------------------------------------------------------------- -// Functions for FMI for Co-Simulation -// --------------------------------------------------------------------------- -/* Simulating the slave */ -fmi2Status fmi2SetRealInputDerivatives(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, - const fmi2Integer order[], const fmi2Real value[]) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2SetRealInputDerivatives", MASK_fmi2SetRealInputDerivatives)) { - return fmi2Error; - } - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetRealInputDerivatives: nvr= %d", nvr) - FILTERED_LOG(comp, fmi2Error, LOG_ERROR, "fmi2SetRealInputDerivatives: ignoring function call." - " This model cannot interpolate inputs: canInterpolateInputs=\"fmi2False\"") - return fmi2Error; -} - -fmi2Status fmi2GetRealOutputDerivatives(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, - const fmi2Integer order[], fmi2Real value[]) { - int i; - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2GetRealOutputDerivatives", MASK_fmi2GetRealOutputDerivatives)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2GetRealOutputDerivatives: nvr= %d", nvr) - FILTERED_LOG(comp, fmi2Error, LOG_ERROR,"fmi2GetRealOutputDerivatives: ignoring function call." - " This model cannot compute derivatives of outputs: MaxOutputDerivativeOrder=\"0\"") - for (i = 0; i < nvr; i++) value[i] = 0; - return fmi2Error; -} - -fmi2Status fmi2CancelStep(fmi2Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2CancelStep", MASK_fmi2CancelStep)) { - // always fmi2CancelStep is invalid, because model is never in modelStepInProgress state. - return fmi2Error; - } - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2CancelStep") - FILTERED_LOG(comp, fmi2Error, LOG_ERROR,"fmi2CancelStep: Can be called when fmi2DoStep returned fmi2Pending." - " This is not the case."); - // comp->state = modelStepCanceled; - return fmi2Error; -} - -fmi2Status fmi2DoStep(fmi2Component c, fmi2Real currentCommunicationPoint, - fmi2Real communicationStepSize, fmi2Boolean noSetFMUStatePriorToCurrentPoint) { - ModelInstance *comp = (ModelInstance *)c; - - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2DoStep: " - "currentCommunicationPoint = %g, " - "communicationStepSize = %g, " - "noSetFMUStatePriorToCurrentPoint = fmi2%s", - currentCommunicationPoint, communicationStepSize, noSetFMUStatePriorToCurrentPoint ? "True" : "False") - - if (communicationStepSize <= 0) { - FILTERED_LOG(comp, fmi2Error, LOG_ERROR, - "fmi2DoStep: communication step size must be > 0. Fount %g.", communicationStepSize) - comp->state = modelError; - return fmi2Error; - } - - return doStep(comp, currentCommunicationPoint, currentCommunicationPoint + communicationStepSize); -} - -/* Inquire slave status */ -static fmi2Status getStatus(char* fname, fmi2Component c, const fmi2StatusKind s) { - const char *statusKind[3] = {"fmi2DoStepStatus","fmi2PendingStatus","fmi2LastSuccessfulTime"}; - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, fname, MASK_fmi2GetStatus)) // all get status have the same MASK_fmi2GetStatus - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "$s: fmi2StatusKind = %s", fname, statusKind[s]) - - switch(s) { - case fmi2DoStepStatus: FILTERED_LOG(comp, fmi2Error, LOG_ERROR, - "%s: Can be called with fmi2DoStepStatus when fmi2DoStep returned fmi2Pending." - " This is not the case.", fname) - break; - case fmi2PendingStatus: FILTERED_LOG(comp, fmi2Error, LOG_ERROR, - "%s: Can be called with fmi2PendingStatus when fmi2DoStep returned fmi2Pending." - " This is not the case.", fname) - break; - case fmi2LastSuccessfulTime: FILTERED_LOG(comp, fmi2Error, LOG_ERROR, - "%s: Can be called with fmi2LastSuccessfulTime when fmi2DoStep returned fmi2Discard." - " This is not the case.", fname) - break; - case fmi2Terminated: FILTERED_LOG(comp, fmi2Error, LOG_ERROR, - "%s: Can be called with fmi2Terminated when fmi2DoStep returned fmi2Discard." - " This is not the case.", fname) - break; - } - return fmi2Discard; -} - -fmi2Status fmi2GetStatus(fmi2Component c, const fmi2StatusKind s, fmi2Status *value) { - return getStatus("fmi2GetStatus", c, s); -} - -fmi2Status fmi2GetRealStatus(fmi2Component c, const fmi2StatusKind s, fmi2Real *value) { - if (s == fmi2LastSuccessfulTime) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2GetRealStatus", MASK_fmi2GetRealStatus)) - return fmi2Error; - *value = comp->time; - return fmi2OK; - } - return getStatus("fmi2GetRealStatus", c, s); -} - -fmi2Status fmi2GetIntegerStatus(fmi2Component c, const fmi2StatusKind s, fmi2Integer *value) { - return getStatus("fmi2GetIntegerStatus", c, s); -} - -fmi2Status fmi2GetBooleanStatus(fmi2Component c, const fmi2StatusKind s, fmi2Boolean *value) { - if (s == fmi2Terminated) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2GetBooleanStatus", MASK_fmi2GetBooleanStatus)) - return fmi2Error; - *value = comp->terminateSimulation; - return fmi2OK; - } - return getStatus("fmi2GetBooleanStatus", c, s); -} - -fmi2Status fmi2GetStringStatus(fmi2Component c, const fmi2StatusKind s, fmi2String *value) { - return getStatus("fmi2GetStringStatus", c, s); -} - -// --------------------------------------------------------------------------- -// Functions for FMI2 for Model Exchange -// --------------------------------------------------------------------------- -/* Enter and exit the different modes */ -fmi2Status fmi2EnterEventMode(fmi2Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2EnterEventMode", MASK_fmi2EnterEventMode)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2EnterEventMode") - - comp->state = modelEventMode; - comp->isNewEventIteration = fmi2True; - return fmi2OK; -} - -fmi2Status fmi2NewDiscreteStates(fmi2Component c, fmi2EventInfo *eventInfo) { - ModelInstance *comp = (ModelInstance *)c; - int timeEvent = 0; - if (invalidState(comp, "fmi2NewDiscreteStates", MASK_fmi2NewDiscreteStates)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2NewDiscreteStates") - - comp->newDiscreteStatesNeeded = fmi2False; - comp->terminateSimulation = fmi2False; - comp->nominalsOfContinuousStatesChanged = fmi2False; - comp->valuesOfContinuousStatesChanged = fmi2False; - - if (comp->nextEventTimeDefined && comp->nextEventTime <= comp->time) { - timeEvent = 1; - } - - eventUpdate(comp); - - comp->isNewEventIteration = false; - - // copy internal eventInfo of component to output eventInfo - eventInfo->newDiscreteStatesNeeded = comp->newDiscreteStatesNeeded; - eventInfo->terminateSimulation = comp->terminateSimulation; - eventInfo->nominalsOfContinuousStatesChanged = comp->nominalsOfContinuousStatesChanged; - eventInfo->valuesOfContinuousStatesChanged = comp->valuesOfContinuousStatesChanged; - eventInfo->nextEventTimeDefined = comp->nextEventTimeDefined; - eventInfo->nextEventTime = comp->nextEventTime; - - return fmi2OK; -} - -fmi2Status fmi2EnterContinuousTimeMode(fmi2Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2EnterContinuousTimeMode", MASK_fmi2EnterContinuousTimeMode)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL,"fmi2EnterContinuousTimeMode") - - comp->state = modelContinuousTimeMode; - return fmi2OK; -} - -fmi2Status fmi2CompletedIntegratorStep(fmi2Component c, fmi2Boolean noSetFMUStatePriorToCurrentPoint, - fmi2Boolean *enterEventMode, fmi2Boolean *terminateSimulation) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2CompletedIntegratorStep", MASK_fmi2CompletedIntegratorStep)) - return fmi2Error; - if (nullPointer(comp, "fmi2CompletedIntegratorStep", "enterEventMode", enterEventMode)) - return fmi2Error; - if (nullPointer(comp, "fmi2CompletedIntegratorStep", "terminateSimulation", terminateSimulation)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL,"fmi2CompletedIntegratorStep") - *enterEventMode = fmi2False; - *terminateSimulation = fmi2False; - return fmi2OK; -} - -/* Providing independent variables and re-initialization of caching */ -fmi2Status fmi2SetTime(fmi2Component c, fmi2Real time) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2SetTime", MASK_fmi2SetTime)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetTime: time=%.16g", time) - comp->time = time; - return fmi2OK; -} - -fmi2Status fmi2SetContinuousStates(fmi2Component c, const fmi2Real x[], size_t nx){ - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2SetContinuousStates", MASK_fmi2SetContinuousStates)) - return fmi2Error; - - if (invalidNumber(comp, "fmi2SetContinuousStates", "nx", nx, NUMBER_OF_STATES)) - return fmi2Error; - - if (nullPointer(comp, "fmi2SetContinuousStates", "x[]", x)) - return fmi2Error; - - setContinuousStates(comp, x, nx); - -//#if NUMBER_OF_STATES>0 -// for (i = 0; i < nx; i++) { -// fmi2ValueReference vr = vrStates[i]; -// FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2SetContinuousStates: #r%d#=%.16g", vr, x[i]) -// assert(vr < NUMBER_OF_REALS); -// comp->r[vr] = x[i]; -// } -//#endif - return fmi2OK; -} - -/* Evaluation of the model equations */ -fmi2Status fmi2GetDerivatives(fmi2Component c, fmi2Real derivatives[], size_t nx) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2GetDerivatives", MASK_fmi2GetDerivatives)) - return fmi2Error; - - if (invalidNumber(comp, "fmi2GetDerivatives", "nx", nx, NUMBER_OF_STATES)) - return fmi2Error; - - if (nullPointer(comp, "fmi2GetDerivatives", "derivatives[]", derivatives)) - return fmi2Error; - - getDerivatives(comp, derivatives, nx); - - return fmi2OK; -} - -fmi2Status fmi2GetEventIndicators(fmi2Component c, fmi2Real eventIndicators[], size_t ni) { - -#if NUMBER_OF_EVENT_INDICATORS > 0 - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2GetEventIndicators", MASK_fmi2GetEventIndicators)) - return fmi2Error; - - if (invalidNumber(comp, "fmi2GetEventIndicators", "ni", ni, NUMBER_OF_EVENT_INDICATORS)) - return fmi2Error; - - getEventIndicators(comp, eventIndicators, ni); -#else - if (ni > 0) return fmi2Error; -#endif - return fmi2OK; -} - -fmi2Status fmi2GetContinuousStates(fmi2Component c, fmi2Real states[], size_t nx) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi2GetContinuousStates", MASK_fmi2GetContinuousStates)) - return fmi2Error; - - if (invalidNumber(comp, "fmi2GetContinuousStates", "nx", nx, NUMBER_OF_STATES)) - return fmi2Error; - - if (nullPointer(comp, "fmi2GetContinuousStates", "states[]", states)) - return fmi2Error; - - getContinuousStates(comp, states, nx); - - return fmi2OK; -} - -fmi2Status fmi2GetNominalsOfContinuousStates(fmi2Component c, fmi2Real x_nominal[], size_t nx) { - int i; - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi2GetNominalsOfContinuousStates", MASK_fmi2GetNominalsOfContinuousStates)) - return fmi2Error; - if (invalidNumber(comp, "fmi2GetNominalContinuousStates", "nx", nx, NUMBER_OF_STATES)) - return fmi2Error; - if (nullPointer(comp, "fmi2GetNominalContinuousStates", "x_nominal[]", x_nominal)) - return fmi2Error; - FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2GetNominalContinuousStates: x_nominal[0..%d] = 1.0", nx-1) - for (i = 0; i < nx; i++) - x_nominal[i] = 1; - return fmi2OK; -} +/**************************************************************** + * Copyright (c) Dassault Systemes. All rights reserved. * + * This file is part of the Test-FMUs. See LICENSE.txt in the * + * project root for license information. * + ****************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "model.h" +#include "slave.h" + + +// C-code FMUs have functions names prefixed with MODEL_IDENTIFIER_. +// Define DISABLE_PREFIX to build a binary FMU. +#ifndef DISABLE_PREFIX +#define pasteA(a,b) a ## b +#define pasteB(a,b) pasteA(a,b) +#define FMI2_FUNCTION_PREFIX pasteB(MODEL_IDENTIFIER, _) +#endif +#include "fmi2Functions.h" + +#ifndef max +#define max(a,b) ((a)>(b) ? (a) : (b)) +#endif + +#ifndef DT_EVENT_DETECT +#define DT_EVENT_DETECT 1e-10 +#endif + +// --------------------------------------------------------------------------- +// Function calls allowed state masks for both Model-exchange and Co-simulation +// --------------------------------------------------------------------------- +#define MASK_fmi2GetTypesPlatform (modelStartAndEnd | modelInstantiated | modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepInProgress | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi2GetVersion MASK_fmi2GetTypesPlatform +#define MASK_fmi2SetDebugLogging (modelInstantiated | modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepInProgress | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi2Instantiate (modelStartAndEnd) +#define MASK_fmi2FreeInstance (modelInstantiated | modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi2SetupExperiment modelInstantiated +#define MASK_fmi2EnterInitializationMode modelInstantiated +#define MASK_fmi2ExitInitializationMode modelInitializationMode +#define MASK_fmi2Terminate (modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepFailed) +#define MASK_fmi2Reset MASK_fmi2FreeInstance +#define MASK_fmi2GetReal (modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi2GetInteger MASK_fmi2GetReal +#define MASK_fmi2GetBoolean MASK_fmi2GetReal +#define MASK_fmi2GetString MASK_fmi2GetReal +#define MASK_fmi2SetReal (modelInstantiated | modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete) +#define MASK_fmi2SetInteger (modelInstantiated | modelInitializationMode \ +| modelEventMode \ +| modelStepComplete) +#define MASK_fmi2SetBoolean MASK_fmi2SetInteger +#define MASK_fmi2SetString MASK_fmi2SetInteger +#define MASK_fmi2GetFMUstate MASK_fmi2FreeInstance +#define MASK_fmi2SetFMUstate MASK_fmi2FreeInstance +#define MASK_fmi2FreeFMUstate MASK_fmi2FreeInstance +#define MASK_fmi2SerializedFMUstateSize MASK_fmi2FreeInstance +#define MASK_fmi2SerializeFMUstate MASK_fmi2FreeInstance +#define MASK_fmi2DeSerializeFMUstate MASK_fmi2FreeInstance +#define MASK_fmi2GetDirectionalDerivative (modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) + +// --------------------------------------------------------------------------- +// Function calls allowed state masks for Model-exchange +// --------------------------------------------------------------------------- +#define MASK_fmi2EnterEventMode (modelEventMode | modelContinuousTimeMode) +#define MASK_fmi2NewDiscreteStates modelEventMode +#define MASK_fmi2EnterContinuousTimeMode modelEventMode +#define MASK_fmi2CompletedIntegratorStep modelContinuousTimeMode +#define MASK_fmi2SetTime (modelEventMode | modelContinuousTimeMode) +#define MASK_fmi2SetContinuousStates modelContinuousTimeMode +#define MASK_fmi2GetEventIndicators (modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelTerminated | modelError) +#define MASK_fmi2GetContinuousStates MASK_fmi2GetEventIndicators +#define MASK_fmi2GetDerivatives (modelEventMode | modelContinuousTimeMode \ +| modelTerminated | modelError) +#define MASK_fmi2GetNominalsOfContinuousStates ( modelInstantiated \ +| modelEventMode | modelContinuousTimeMode \ +| modelTerminated | modelError) + +// --------------------------------------------------------------------------- +// Function calls allowed state masks for Co-simulation +// --------------------------------------------------------------------------- +#define MASK_fmi2SetRealInputDerivatives (modelInstantiated | modelInitializationMode \ +| modelStepComplete) +#define MASK_fmi2GetRealOutputDerivatives (modelStepComplete | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi2DoStep modelStepComplete +#define MASK_fmi2CancelStep modelStepInProgress +#define MASK_fmi2GetStatus (modelStepComplete | modelStepInProgress | modelStepFailed \ +| modelTerminated) +#define MASK_fmi2GetRealStatus MASK_fmi2GetStatus +#define MASK_fmi2GetIntegerStatus MASK_fmi2GetStatus +#define MASK_fmi2GetBooleanStatus MASK_fmi2GetStatus +#define MASK_fmi2GetStringStatus MASK_fmi2GetStatus + +// --------------------------------------------------------------------------- +// Private helpers used below to validate function arguments +// --------------------------------------------------------------------------- + +static fmi2Status unsupportedFunction(fmi2Component c, const char *fName, int statesExpected) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, fName, statesExpected)) + return fmi2Error; + logError(comp, "%s: Function not implemented.", fName); + return fmi2Error; +} + +// --------------------------------------------------------------------------- +// FMI functions +// --------------------------------------------------------------------------- + +fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2String fmuGUID, + fmi2String fmuResourceLocation, const fmi2CallbackFunctions *functions, + fmi2Boolean visible, fmi2Boolean loggingOn) { + + return createModelInstance( + (loggerType)functions->logger, + (allocateMemoryType)functions->allocateMemory, + (freeMemoryType)functions->freeMemory, + functions->componentEnvironment, + instanceName, + fmuGUID, + fmuResourceLocation, + loggingOn, + fmuType + ); +} + +fmi2Status fmi2SetupExperiment(fmi2Component c, fmi2Boolean toleranceDefined, fmi2Real tolerance, + fmi2Real startTime, fmi2Boolean stopTimeDefined, fmi2Real stopTime) { + + // ignore arguments: stopTimeDefined, stopTime + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2SetupExperiment", MASK_fmi2SetupExperiment)) + return fmi2Error; + + comp->time = startTime; + + return fmi2OK; +} + +fmi2Status fmi2EnterInitializationMode(fmi2Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2EnterInitializationMode", MASK_fmi2EnterInitializationMode)) + return fmi2Error; + comp->state = modelInitializationMode; + return fmi2OK; +} + +fmi2Status fmi2ExitInitializationMode(fmi2Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2ExitInitializationMode", MASK_fmi2ExitInitializationMode)) + return fmi2Error; + + // if values were set and no fmi2GetXXX triggered update before, + // ensure calculated values are updated now + if (comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + if (comp->type == ModelExchange) { + comp->state = modelEventMode; + comp->isNewEventIteration = false; + } + else comp->state = modelStepComplete; + return fmi2OK; +} + +fmi2Status fmi2Terminate(fmi2Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2Terminate", MASK_fmi2Terminate)) + return fmi2Error; + comp->state = modelTerminated; + return fmi2OK; +} + +fmi2Status fmi2Reset(fmi2Component c) { + ModelInstance* comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2Reset", MASK_fmi2Reset)) + return fmi2Error; + comp->state = modelInstantiated; + setStartValues(comp); // to be implemented by the includer of this file + comp->isDirtyValues = true; // because we just called setStartValues + return fmi2OK; +} + +void fmi2FreeInstance(fmi2Component c) { + + ModelInstance *comp = (ModelInstance *)c; + + if (!comp) return; + + if (invalidState(comp, "fmi2FreeInstance", MASK_fmi2FreeInstance)) + return; + + freeModelInstance(comp); +} + +// --------------------------------------------------------------------------- +// FMI functions: class methods not depending of a specific model instance +// --------------------------------------------------------------------------- + +const char* fmi2GetVersion() { + return fmi2Version; +} + +const char* fmi2GetTypesPlatform() { + return fmi2TypesPlatform; +} + +// --------------------------------------------------------------------------- +// FMI functions: logging control, setters and getters for Real, Integer, +// Boolean, String +// --------------------------------------------------------------------------- + +fmi2Status fmi2SetDebugLogging(fmi2Component c, fmi2Boolean loggingOn, size_t nCategories, const fmi2String categories[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2SetDebugLogging", MASK_fmi2SetDebugLogging)) return fmi2Error; + + return setDebugLogging(comp, loggingOn, nCategories, categories); +} + +fmi2Status fmi2GetReal (fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2Real value[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2GetReal", MASK_fmi2GetReal)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2GetReal", "vr[]", vr)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2GetReal", "value[]", value)) + return fmi2Error; + + if (nvr > 0 && comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + GET_VARIABLES(Float64) +} + +fmi2Status fmi2GetInteger(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2Integer value[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2GetInteger", MASK_fmi2GetInteger)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2GetInteger", "vr[]", vr)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2GetInteger", "value[]", value)) + return fmi2Error; + + if (nvr > 0 && comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + GET_VARIABLES(Int32) +} + +fmi2Status fmi2GetBoolean(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2Boolean value[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2GetBoolean", MASK_fmi2GetBoolean)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2GetBoolean", "vr[]", vr)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2GetBoolean", "value[]", value)) + return fmi2Error; + + if (nvr > 0 && comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + GET_BOOLEAN_VARIABLES +} + +fmi2Status fmi2GetString (fmi2Component c, const fmi2ValueReference vr[], size_t nvr, fmi2String value[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2GetString", MASK_fmi2GetString)) + return fmi2Error; + + if (nvr>0 && nullPointer(comp, "fmi2GetString", "vr[]", vr)) + return fmi2Error; + + if (nvr>0 && nullPointer(comp, "fmi2GetString", "value[]", value)) + return fmi2Error; + + if (nvr > 0 && comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + GET_VARIABLES(String) +} + +fmi2Status fmi2SetReal (fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Real value[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2SetReal", MASK_fmi2SetReal)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2SetReal", "vr[]", vr)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2SetReal", "value[]", value)) + return fmi2Error; + + SET_VARIABLES(Float64) +} + +fmi2Status fmi2SetInteger(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Integer value[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2SetInteger", MASK_fmi2SetInteger)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2SetInteger", "vr[]", vr)) + return fmi2Error; + + if (nvr > 0 && nullPointer(comp, "fmi2SetInteger", "value[]", value)) + return fmi2Error; + + SET_VARIABLES(Int32) +} + +fmi2Status fmi2SetBoolean(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2Boolean value[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2SetBoolean", MASK_fmi2SetBoolean)) + return fmi2Error; + + if (nvr>0 && nullPointer(comp, "fmi2SetBoolean", "vr[]", vr)) + return fmi2Error; + + if (nvr>0 && nullPointer(comp, "fmi2SetBoolean", "value[]", value)) + return fmi2Error; + + SET_BOOLEAN_VARIABLES +} + +fmi2Status fmi2SetString (fmi2Component c, const fmi2ValueReference vr[], size_t nvr, const fmi2String value[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2SetString", MASK_fmi2SetString)) + return fmi2Error; + + if (nvr>0 && nullPointer(comp, "fmi2SetString", "vr[]", vr)) + return fmi2Error; + + if (nvr>0 && nullPointer(comp, "fmi2SetString", "value[]", value)) + return fmi2Error; + + SET_VARIABLES(String) +} + +fmi2Status fmi2GetFMUstate (fmi2Component c, fmi2FMUstate* FMUstate) { + ModelInstance *comp = (ModelInstance *)c; + ModelData *modelData = comp->allocateMemory(1, sizeof(ModelData)); + memcpy(modelData, comp->modelData, sizeof(ModelData)); + *FMUstate = modelData; + return fmi2OK; +} + +fmi2Status fmi2SetFMUstate (fmi2Component c, fmi2FMUstate FMUstate) { + ModelInstance *comp = (ModelInstance *)c; + ModelData *modelData = FMUstate; + memcpy(comp->modelData, modelData, sizeof(ModelData)); + return fmi2OK; +} + +fmi2Status fmi2FreeFMUstate(fmi2Component c, fmi2FMUstate* FMUstate) { + ModelInstance *comp = (ModelInstance *)c; + ModelData *modelData = *FMUstate; + comp->freeMemory(modelData); + *FMUstate = NULL; + return fmi2OK; +} + +fmi2Status fmi2SerializedFMUstateSize(fmi2Component c, fmi2FMUstate FMUstate, size_t *size) { + *size = sizeof(ModelData); + return fmi2OK; +} + +fmi2Status fmi2SerializeFMUstate (fmi2Component c, fmi2FMUstate FMUstate, fmi2Byte serializedState[], size_t size) { + ModelInstance *comp = (ModelInstance *)c; + // TODO: check size + memcpy(serializedState, comp->modelData, sizeof(ModelData)); + return fmi2OK; +} + +fmi2Status fmi2DeSerializeFMUstate (fmi2Component c, const fmi2Byte serializedState[], size_t size, fmi2FMUstate* FMUstate) { + + ModelInstance *comp = (ModelInstance *)c; + + if (*FMUstate == NULL) { + *FMUstate = comp->allocateMemory(1, sizeof(ModelData)); + } + + // TODO: check size + + memcpy(*FMUstate, serializedState, sizeof(ModelData)); + + return fmi2OK; +} + +fmi2Status fmi2GetDirectionalDerivative(fmi2Component c, const fmi2ValueReference vUnknown_ref[], size_t nUnknown, + const fmi2ValueReference vKnown_ref[] , size_t nKnown, + const fmi2Real dvKnown[], fmi2Real dvUnknown[]) { + return unsupportedFunction(c, "fmi2GetDirectionalDerivative", MASK_fmi2GetDirectionalDerivative); +} + +// --------------------------------------------------------------------------- +// Functions for FMI for Co-Simulation +// --------------------------------------------------------------------------- +/* Simulating the slave */ +fmi2Status fmi2SetRealInputDerivatives(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, + const fmi2Integer order[], const fmi2Real value[]) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2SetRealInputDerivatives", MASK_fmi2SetRealInputDerivatives)) { + return fmi2Error; + } + logError(comp, "fmi2SetRealInputDerivatives: ignoring function call." + " This model cannot interpolate inputs: canInterpolateInputs=\"fmi2False\""); + return fmi2Error; +} + +fmi2Status fmi2GetRealOutputDerivatives(fmi2Component c, const fmi2ValueReference vr[], size_t nvr, + const fmi2Integer order[], fmi2Real value[]) { + int i; + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2GetRealOutputDerivatives", MASK_fmi2GetRealOutputDerivatives)) + return fmi2Error; + logError(comp, "fmi2GetRealOutputDerivatives: ignoring function call." + " This model cannot compute derivatives of outputs: MaxOutputDerivativeOrder=\"0\""); + for (i = 0; i < nvr; i++) value[i] = 0; + return fmi2Error; +} + +fmi2Status fmi2CancelStep(fmi2Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2CancelStep", MASK_fmi2CancelStep)) { + // always fmi2CancelStep is invalid, because model is never in modelStepInProgress state. + return fmi2Error; + } + logError(comp, "fmi2CancelStep: Can be called when fmi2DoStep returned fmi2Pending." + " This is not the case."); + return fmi2Error; +} + +fmi2Status fmi2DoStep(fmi2Component c, fmi2Real currentCommunicationPoint, + fmi2Real communicationStepSize, fmi2Boolean noSetFMUStatePriorToCurrentPoint) { + ModelInstance *comp = (ModelInstance *)c; + + if (communicationStepSize <= 0) { + logError(comp, "fmi2DoStep: communication step size must be > 0. Fount %g.", communicationStepSize); + comp->state = modelError; + return fmi2Error; + } + + return doStep(comp, currentCommunicationPoint, currentCommunicationPoint + communicationStepSize); +} + +/* Inquire slave status */ +static fmi2Status getStatus(char* fname, fmi2Component c, const fmi2StatusKind s) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, fname, MASK_fmi2GetStatus)) // all get status have the same MASK_fmi2GetStatus + return fmi2Error; + + switch(s) { + case fmi2DoStepStatus: logError(comp, + "%s: Can be called with fmi2DoStepStatus when fmi2DoStep returned fmi2Pending." + " This is not the case.", fname); + break; + case fmi2PendingStatus: logError(comp, + "%s: Can be called with fmi2PendingStatus when fmi2DoStep returned fmi2Pending." + " This is not the case.", fname); + break; + case fmi2LastSuccessfulTime: logError(comp, + "%s: Can be called with fmi2LastSuccessfulTime when fmi2DoStep returned fmi2Discard." + " This is not the case.", fname); + break; + case fmi2Terminated: logError(comp, + "%s: Can be called with fmi2Terminated when fmi2DoStep returned fmi2Discard." + " This is not the case.", fname); + break; + } + + return fmi2Discard; +} + +fmi2Status fmi2GetStatus(fmi2Component c, const fmi2StatusKind s, fmi2Status *value) { + return getStatus("fmi2GetStatus", c, s); +} + +fmi2Status fmi2GetRealStatus(fmi2Component c, const fmi2StatusKind s, fmi2Real *value) { + if (s == fmi2LastSuccessfulTime) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2GetRealStatus", MASK_fmi2GetRealStatus)) + return fmi2Error; + *value = comp->time; + return fmi2OK; + } + return getStatus("fmi2GetRealStatus", c, s); +} + +fmi2Status fmi2GetIntegerStatus(fmi2Component c, const fmi2StatusKind s, fmi2Integer *value) { + return getStatus("fmi2GetIntegerStatus", c, s); +} + +fmi2Status fmi2GetBooleanStatus(fmi2Component c, const fmi2StatusKind s, fmi2Boolean *value) { + if (s == fmi2Terminated) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2GetBooleanStatus", MASK_fmi2GetBooleanStatus)) + return fmi2Error; + *value = comp->terminateSimulation; + return fmi2OK; + } + return getStatus("fmi2GetBooleanStatus", c, s); +} + +fmi2Status fmi2GetStringStatus(fmi2Component c, const fmi2StatusKind s, fmi2String *value) { + return getStatus("fmi2GetStringStatus", c, s); +} + +// --------------------------------------------------------------------------- +// Functions for FMI2 for Model Exchange +// --------------------------------------------------------------------------- +/* Enter and exit the different modes */ +fmi2Status fmi2EnterEventMode(fmi2Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2EnterEventMode", MASK_fmi2EnterEventMode)) + return fmi2Error; + comp->state = modelEventMode; + comp->isNewEventIteration = fmi2True; + return fmi2OK; +} + +fmi2Status fmi2NewDiscreteStates(fmi2Component c, fmi2EventInfo *eventInfo) { + ModelInstance *comp = (ModelInstance *)c; + int timeEvent = 0; + if (invalidState(comp, "fmi2NewDiscreteStates", MASK_fmi2NewDiscreteStates)) + return fmi2Error; + comp->newDiscreteStatesNeeded = fmi2False; + comp->terminateSimulation = fmi2False; + comp->nominalsOfContinuousStatesChanged = fmi2False; + comp->valuesOfContinuousStatesChanged = fmi2False; + + if (comp->nextEventTimeDefined && comp->nextEventTime <= comp->time) { + timeEvent = 1; + } + + eventUpdate(comp); + + comp->isNewEventIteration = false; + + // copy internal eventInfo of component to output eventInfo + eventInfo->newDiscreteStatesNeeded = comp->newDiscreteStatesNeeded; + eventInfo->terminateSimulation = comp->terminateSimulation; + eventInfo->nominalsOfContinuousStatesChanged = comp->nominalsOfContinuousStatesChanged; + eventInfo->valuesOfContinuousStatesChanged = comp->valuesOfContinuousStatesChanged; + eventInfo->nextEventTimeDefined = comp->nextEventTimeDefined; + eventInfo->nextEventTime = comp->nextEventTime; + + return fmi2OK; +} + +fmi2Status fmi2EnterContinuousTimeMode(fmi2Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2EnterContinuousTimeMode", MASK_fmi2EnterContinuousTimeMode)) + return fmi2Error; + comp->state = modelContinuousTimeMode; + return fmi2OK; +} + +fmi2Status fmi2CompletedIntegratorStep(fmi2Component c, fmi2Boolean noSetFMUStatePriorToCurrentPoint, + fmi2Boolean *enterEventMode, fmi2Boolean *terminateSimulation) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2CompletedIntegratorStep", MASK_fmi2CompletedIntegratorStep)) + return fmi2Error; + if (nullPointer(comp, "fmi2CompletedIntegratorStep", "enterEventMode", enterEventMode)) + return fmi2Error; + if (nullPointer(comp, "fmi2CompletedIntegratorStep", "terminateSimulation", terminateSimulation)) + return fmi2Error; + *enterEventMode = fmi2False; + *terminateSimulation = fmi2False; + return fmi2OK; +} + +/* Providing independent variables and re-initialization of caching */ +fmi2Status fmi2SetTime(fmi2Component c, fmi2Real time) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2SetTime", MASK_fmi2SetTime)) + return fmi2Error; + comp->time = time; + return fmi2OK; +} + +fmi2Status fmi2SetContinuousStates(fmi2Component c, const fmi2Real x[], size_t nx){ + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2SetContinuousStates", MASK_fmi2SetContinuousStates)) + return fmi2Error; + + if (invalidNumber(comp, "fmi2SetContinuousStates", "nx", nx, NUMBER_OF_STATES)) + return fmi2Error; + + if (nullPointer(comp, "fmi2SetContinuousStates", "x[]", x)) + return fmi2Error; + + setContinuousStates(comp, x, nx); + + return fmi2OK; +} + +/* Evaluation of the model equations */ +fmi2Status fmi2GetDerivatives(fmi2Component c, fmi2Real derivatives[], size_t nx) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2GetDerivatives", MASK_fmi2GetDerivatives)) + return fmi2Error; + + if (invalidNumber(comp, "fmi2GetDerivatives", "nx", nx, NUMBER_OF_STATES)) + return fmi2Error; + + if (nullPointer(comp, "fmi2GetDerivatives", "derivatives[]", derivatives)) + return fmi2Error; + + getDerivatives(comp, derivatives, nx); + + return fmi2OK; +} + +fmi2Status fmi2GetEventIndicators(fmi2Component c, fmi2Real eventIndicators[], size_t ni) { + +#if NUMBER_OF_EVENT_INDICATORS > 0 + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2GetEventIndicators", MASK_fmi2GetEventIndicators)) + return fmi2Error; + + if (invalidNumber(comp, "fmi2GetEventIndicators", "ni", ni, NUMBER_OF_EVENT_INDICATORS)) + return fmi2Error; + + getEventIndicators(comp, eventIndicators, ni); +#else + if (ni > 0) return fmi2Error; +#endif + return fmi2OK; +} + +fmi2Status fmi2GetContinuousStates(fmi2Component c, fmi2Real states[], size_t nx) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi2GetContinuousStates", MASK_fmi2GetContinuousStates)) + return fmi2Error; + + if (invalidNumber(comp, "fmi2GetContinuousStates", "nx", nx, NUMBER_OF_STATES)) + return fmi2Error; + + if (nullPointer(comp, "fmi2GetContinuousStates", "states[]", states)) + return fmi2Error; + + getContinuousStates(comp, states, nx); + + return fmi2OK; +} + +fmi2Status fmi2GetNominalsOfContinuousStates(fmi2Component c, fmi2Real x_nominal[], size_t nx) { + int i; + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi2GetNominalsOfContinuousStates", MASK_fmi2GetNominalsOfContinuousStates)) + return fmi2Error; + if (invalidNumber(comp, "fmi2GetNominalContinuousStates", "nx", nx, NUMBER_OF_STATES)) + return fmi2Error; + if (nullPointer(comp, "fmi2GetNominalContinuousStates", "x_nominal[]", x_nominal)) + return fmi2Error; + for (i = 0; i < nx; i++) + x_nominal[i] = 1; + return fmi2OK; +} diff --git a/src/fmi3.c b/src/fmi3Functions.c similarity index 61% rename from src/fmi3.c rename to src/fmi3Functions.c index e40f686..b1b3e7e 100644 --- a/src/fmi3.c +++ b/src/fmi3Functions.c @@ -1,863 +1,608 @@ -/**************************************************************** - * Copyright (c) Dassault Systemes. All rights reserved. * - * This file is part of the Test-FMUs. See LICENSE.txt in the * - * project root for license information. * - ****************************************************************/ - -#include -#include -#include -#include - -#include "config.h" -#include "model.h" -#include "solver.h" -#include "slave.h" - - -// C-code FMUs have functions names prefixed with MODEL_IDENTIFIER_. -// Define DISABLE_PREFIX to build a binary FMU. -#ifndef DISABLE_PREFIX -#define pasteA(a,b) a ## b -#define pasteB(a,b) pasteA(a,b) -#define fmi3_FUNCTION_PREFIX pasteB(MODEL_IDENTIFIER, _) -#endif -#include "fmi3Functions.h" - - -// macro to be used to log messages. The macro check if current -// log category is valid and, if true, call the logger provided by simulator. -#define FILTERED_LOG(instance, status, categoryIndex, message, ...) if (status == fmi3Error || status == fmi3Fatal || isCategoryLogged(instance, categoryIndex)) \ - instance->logger(instance->componentEnvironment, instance->instanceName, status, \ - logCategoriesNames[categoryIndex], message, ##__VA_ARGS__); - -static const char *logCategoriesNames[] = {"logAll", "logError", "logFmiCall", "logEvent"}; - -#ifndef max -#define max(a,b) ((a)>(b) ? (a) : (b)) -#endif - -#ifndef DT_EVENT_DETECT -#define DT_EVENT_DETECT 1e-10 -#endif - -// --------------------------------------------------------------------------- -// Function calls allowed state masks for both Model-exchange and Co-simulation -// --------------------------------------------------------------------------- -#define MASK_fmi3GetTypesPlatform (modelStartAndEnd | modelInstantiated | modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepInProgress | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi3GetVersion MASK_fmi3GetTypesPlatform -#define MASK_fmi3SetDebugLogging (modelInstantiated | modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepInProgress | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi3Instantiate (modelStartAndEnd) -#define MASK_fmi3FreeInstance (modelInstantiated | modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi3SetupExperiment modelInstantiated -#define MASK_fmi3EnterInitializationMode modelInstantiated -#define MASK_fmi3ExitInitializationMode modelInitializationMode -#define MASK_fmi3Terminate (modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepFailed) -#define MASK_fmi3Reset MASK_fmi3FreeInstance -#define MASK_fmi3GetReal (modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi3GetInteger MASK_fmi3GetReal -#define MASK_fmi3GetBoolean MASK_fmi3GetReal -#define MASK_fmi3GetString MASK_fmi3GetReal -#define MASK_fmi3SetReal (modelInstantiated | modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete) -#define MASK_fmi3SetInteger (modelInstantiated | modelInitializationMode \ -| modelEventMode \ -| modelStepComplete) -#define MASK_fmi3SetBoolean MASK_fmi3SetInteger -#define MASK_fmi3SetString MASK_fmi3SetInteger -#define MASK_fmi3GetFMUstate MASK_fmi3FreeInstance -#define MASK_fmi3SetFMUstate MASK_fmi3FreeInstance -#define MASK_fmi3FreeFMUstate MASK_fmi3FreeInstance -#define MASK_fmi3SerializedFMUstateSize MASK_fmi3FreeInstance -#define MASK_fmi3SerializeFMUstate MASK_fmi3FreeInstance -#define MASK_fmi3DeSerializeFMUstate MASK_fmi3FreeInstance -#define MASK_fmi3GetDirectionalDerivative (modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelStepComplete | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) - -// --------------------------------------------------------------------------- -// Function calls allowed state masks for Model-exchange -// --------------------------------------------------------------------------- -#define MASK_fmi3EnterEventMode (modelEventMode | modelContinuousTimeMode) -#define MASK_fmi3NewDiscreteStates modelEventMode -#define MASK_fmi3EnterContinuousTimeMode modelEventMode -#define MASK_fmi3CompletedIntegratorStep modelContinuousTimeMode -#define MASK_fmi3SetTime (modelEventMode | modelContinuousTimeMode) -#define MASK_fmi3SetContinuousStates modelContinuousTimeMode -#define MASK_fmi3GetEventIndicators (modelInitializationMode \ -| modelEventMode | modelContinuousTimeMode \ -| modelTerminated | modelError) -#define MASK_fmi3GetContinuousStates MASK_fmi3GetEventIndicators -#define MASK_fmi3GetDerivatives (modelEventMode | modelContinuousTimeMode \ -| modelTerminated | modelError) -#define MASK_fmi3GetNominalsOfContinuousStates ( modelInstantiated \ -| modelEventMode | modelContinuousTimeMode \ -| modelTerminated | modelError) - -// --------------------------------------------------------------------------- -// Function calls allowed state masks for Co-simulation -// --------------------------------------------------------------------------- -#define MASK_fmi3SetRealInputDerivatives (modelInstantiated | modelInitializationMode \ -| modelStepComplete) -#define MASK_fmi3GetRealOutputDerivatives (modelStepComplete | modelStepFailed | modelStepCanceled \ -| modelTerminated | modelError) -#define MASK_fmi3DoStep modelStepComplete -#define MASK_fmi3CancelStep modelStepInProgress -#define MASK_fmi3GetStatus (modelStepComplete | modelStepInProgress | modelStepFailed \ -| modelTerminated) -#define MASK_fmi3GetRealStatus MASK_fmi3GetStatus -#define MASK_fmi3GetIntegerStatus MASK_fmi3GetStatus -#define MASK_fmi3GetBooleanStatus MASK_fmi3GetStatus -#define MASK_fmi3GetStringStatus MASK_fmi3GetStatus - -// --------------------------------------------------------------------------- -// Private helpers used below to validate function arguments -// --------------------------------------------------------------------------- - -void logError(ModelInstance *comp, const char *message, ...) { - - va_list args; - size_t len = 0; - char *buf = ""; - - va_start(args, message); - len = vsnprintf(buf, len, message, args); - va_end(args); - - buf = malloc(len + 1); - - va_start(args, message); - len = vsnprintf(buf, len + 1, message, args); - va_end(args); - - comp->logger(comp->componentEnvironment, comp->instanceName, fmi3Error, "logError", buf); - - free(buf); -} - -void *allocateMemory(ModelInstance *comp, size_t size) { - return comp->allocateMemory(comp->componentEnvironment, size, 1); -} - -void freeMemory(ModelInstance *comp, void *obj) { - comp->freeMemory(comp->componentEnvironment, obj); -} - -const char *duplicateString(ModelInstance *comp, const char *str1) { - size_t len = strlen(str1); - char *str2 = allocateMemory(comp, len + 1); - strncpy(str2, str1, len + 1); - return str2; -} - -fmi3Boolean isCategoryLogged(ModelInstance *comp, int categoryIndex); - -static bool invalidNumber(ModelInstance *comp, const char *f, const char *arg, int n, int nExpected) { - if (n != nExpected) { - comp->state = modelError; - FILTERED_LOG(comp, fmi3Error, LOG_ERROR, "%s: Invalid argument %s = %d. Expected %d.", f, arg, n, nExpected) - return fmi3True; - } - return fmi3False; -} - -static fmi3Boolean invalidState(ModelInstance *comp, const char *f, int statesExpected) { - if (!comp) - return fmi3True; - if (!(comp->state & statesExpected)) { - comp->state = modelError; - FILTERED_LOG(comp, fmi3Error, LOG_ERROR, "%s: Illegal call sequence.", f) - return fmi3True; - } - return fmi3False; -} - -static fmi3Boolean nullPointer(ModelInstance* comp, const char *f, const char *arg, const void *p) { - if (!p) { - comp->state = modelError; - FILTERED_LOG(comp, fmi3Error, LOG_ERROR, "%s: Invalid argument %s = NULL.", f, arg) - return fmi3True; - } - return fmi3False; -} - -static fmi3Status unsupportedFunction(fmi3Component c, const char *fName, int statesExpected) { - ModelInstance *comp = (ModelInstance *)c; - //fmi3CallbackLogger log = comp->functions->logger; - if (invalidState(comp, fName, statesExpected)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, fName); - FILTERED_LOG(comp, fmi3Error, LOG_ERROR, "%s: Function not implemented.", fName) - return fmi3Error; -} - -// --------------------------------------------------------------------------- -// Private helpers logger -// --------------------------------------------------------------------------- - -// return fmi3True if logging category is on. Else return fmi3False. -fmi3Boolean isCategoryLogged(ModelInstance *comp, int categoryIndex) { - if (categoryIndex < NUMBER_OF_CATEGORIES - && (comp->logCategories[categoryIndex] || comp->logCategories[LOG_ALL])) { - return fmi3True; - } - return fmi3False; -} - -// --------------------------------------------------------------------------- -// FMI functions -// --------------------------------------------------------------------------- -fmi3Component fmi3Instantiate(fmi3String instanceName, fmi3Type fmuType, fmi3String fmuGUID, - fmi3String fmuResourceLocation, const fmi3CallbackFunctions *functions, - fmi3Boolean visible, fmi3Boolean loggingOn) { - // ignoring arguments: fmuResourceLocation, visible - ModelInstance *comp; - - if (!functions->logger) { - return NULL; - } - - if (!functions->allocateMemory || !functions->freeMemory) { - functions->logger(functions->componentEnvironment, instanceName, fmi3Error, "error", - "fmi3Instantiate: Missing callback function."); - return NULL; - } - - if (!instanceName || strlen(instanceName) == 0) { - functions->logger(functions->componentEnvironment, "?", fmi3Error, "error", - "fmi3Instantiate: Missing instance name."); - return NULL; - } - - if (!fmuGUID || strlen(fmuGUID) == 0) { - functions->logger(functions->componentEnvironment, instanceName, fmi3Error, "error", - "fmi3Instantiate: Missing GUID."); - return NULL; - } - - if (strcmp(fmuGUID, MODEL_GUID)) { -// functions->logger(functions->componentEnvironment, instanceName, fmi3Error, "error", -// "fmi3Instantiate: Wrong GUID %s. Expected %s.", fmuGUID, MODEL_GUID); - return NULL; - } - - comp = (ModelInstance *)functions->allocateMemory(NULL, 1, sizeof(ModelInstance)); - - if (comp) { - int i; - comp->instanceName = (char *)functions->allocateMemory(NULL, 1 + strlen(instanceName), sizeof(char)); - comp->GUID = (char *)functions->allocateMemory(NULL, 1 + strlen(fmuGUID), sizeof(char)); - comp->resourceLocation = (char *)functions->allocateMemory(NULL, 1 + strlen(fmuResourceLocation), sizeof(char)); - - comp->modelData = (ModelData *)functions->allocateMemory(NULL, 1, sizeof(ModelData)); - - // set all categories to on or off. fmi3SetDebugLogging should be called to choose specific categories. - for (i = 0; i < NUMBER_OF_CATEGORIES; i++) { - comp->logCategories[i] = loggingOn; - } - } - - if (!comp || !comp->modelData || !comp->instanceName || !comp->GUID) { - functions->logger(functions->componentEnvironment, instanceName, fmi3Error, "error", - "fmi3Instantiate: Out of memory."); - return NULL; - } - - comp->time = 0; // overwrite in fmi3SetupExperiment, fmi3SetTime - strcpy((char *)comp->instanceName, (char *)instanceName); - comp->type = fmuType; - strcpy((char *)comp->GUID, (char *)fmuGUID); - strcpy((char *)comp->resourceLocation, (char *)fmuResourceLocation); - comp->logger = functions->logger; - comp->allocateMemory = functions->allocateMemory; - comp->freeMemory = functions->freeMemory; - comp->stepFinished = functions->stepFinished; - comp->componentEnvironment = functions->componentEnvironment; - comp->loggingOn = loggingOn; - comp->state = modelInstantiated; - comp->isNewEventIteration = fmi3False; - - comp->newDiscreteStatesNeeded = fmi3False; - comp->terminateSimulation = fmi3False; - comp->nominalsOfContinuousStatesChanged = fmi3False; - comp->valuesOfContinuousStatesChanged = fmi3False; - comp->nextEventTimeDefined = fmi3False; - comp->nextEventTime = 0; - - setStartValues(comp); - comp->isDirtyValues = true; - - comp->solverData = solver_create(comp); - - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3Instantiate: GUID=%s", fmuGUID) - - return comp; -} - -fmi3Status fmi3SetupExperiment(fmi3Component c, fmi3Boolean toleranceDefined, fmi3Float64 tolerance, - fmi3Float64 startTime, fmi3Boolean stopTimeDefined, fmi3Float64 stopTime) { - - // ignore arguments: stopTimeDefined, stopTime - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3SetupExperiment", MASK_fmi3SetupExperiment)) - return fmi3Error; - - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3SetupExperiment: toleranceDefined=%d tolerance=%g", - toleranceDefined, tolerance) - - comp->time = startTime; - - return fmi3OK; -} - -fmi3Status fmi3EnterInitializationMode(fmi3Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3EnterInitializationMode", MASK_fmi3EnterInitializationMode)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3EnterInitializationMode") - - comp->state = modelInitializationMode; - return fmi3OK; -} - -fmi3Status fmi3ExitInitializationMode(fmi3Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3ExitInitializationMode", MASK_fmi3ExitInitializationMode)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3ExitInitializationMode") - - // if values were set and no fmi3GetXXX triggered update before, - // ensure calculated values are updated now - if (comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - if (comp->type == fmi3ModelExchange) { - comp->state = modelEventMode; - comp->isNewEventIteration = fmi3True; - } else { - comp->state = modelStepComplete; - } - - return fmi3OK; -} - -fmi3Status fmi3Terminate(fmi3Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3Terminate", MASK_fmi3Terminate)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3Terminate") - - comp->state = modelTerminated; - return fmi3OK; -} - -fmi3Status fmi3Reset(fmi3Component c) { - ModelInstance* comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3Reset", MASK_fmi3Reset)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3Reset") - - comp->state = modelInstantiated; - setStartValues(comp); - comp->isDirtyValues = true; - return fmi3OK; -} - -void fmi3FreeInstance(fmi3Component c) { - - ModelInstance *comp = (ModelInstance *)c; - - if (!comp) return; - - if (invalidState(comp, "fmi3FreeInstance", MASK_fmi3FreeInstance)) - return; - - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3FreeInstance") - - if (comp->instanceName) comp->freeMemory(comp, (void *)comp->instanceName); - if (comp->GUID) comp->freeMemory(comp, (void *)comp->GUID); - comp->freeMemory(comp, comp); -} - -// --------------------------------------------------------------------------- -// FMI functions: class methods not depending of a specific model instance -// --------------------------------------------------------------------------- - -const char* fmi3GetVersion() { - return fmi3Version; -} - -// --------------------------------------------------------------------------- -// FMI functions: logging control, setters and getters for Real, Integer, -// Boolean, String -// --------------------------------------------------------------------------- - -fmi3Status fmi3SetDebugLogging(fmi3Component c, fmi3Boolean loggingOn, size_t nCategories, const fmi3String categories[]) { - int i, j; - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3SetDebugLogging", MASK_fmi3SetDebugLogging)) - return fmi3Error; - comp->loggingOn = loggingOn; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3SetDebugLogging") - - // reset all categories - for (j = 0; j < NUMBER_OF_CATEGORIES; j++) { - comp->logCategories[j] = fmi3False; - } - - if (nCategories == 0) { - // no category specified, set all categories to have loggingOn value - for (j = 0; j < NUMBER_OF_CATEGORIES; j++) { - comp->logCategories[j] = loggingOn; - } - } else { - // set specific categories on - for (i = 0; i < nCategories; i++) { - fmi3Boolean categoryFound = fmi3False; - for (j = 0; j < NUMBER_OF_CATEGORIES; j++) { - if (strcmp(logCategoriesNames[j], categories[i]) == 0) { - comp->logCategories[j] = loggingOn; - categoryFound = fmi3True; - break; - } - } - if (!categoryFound) { - comp->logger(comp->componentEnvironment, comp->instanceName, fmi3Warning, - logCategoriesNames[LOG_ERROR], - "logging category '%s' is not supported by model", categories[i]); - } - } - } - return fmi3OK; -} - -fmi3Status fmi3GetFloat64 (fmi3Component c, const fmi3ValueReference vr[], size_t nvr, fmi3Float64 value[], size_t nValues) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3GetReal", MASK_fmi3GetReal)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3GetReal", "vr[]", vr)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3GetReal", "value[]", value)) - return fmi3Error; - - if (nvr > 0 && comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - GET_VARIABLES(Float64) -} - -fmi3Status fmi3GetInt32(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, fmi3Int32 value[], size_t nValues) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3GetInteger", MASK_fmi3GetInteger)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3GetInteger", "vr[]", vr)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3GetInteger", "value[]", value)) - return fmi3Error; - - if (nvr > 0 && comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - GET_VARIABLES(Int32) -} - -fmi3Status fmi3GetBoolean(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, fmi3Boolean value[], size_t nValues) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3GetBoolean", MASK_fmi3GetBoolean)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3GetBoolean", "vr[]", vr)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3GetBoolean", "value[]", value)) - return fmi3Error; - - if (nvr > 0 && comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - GET_BOOLEAN_VARIABLES -} - -fmi3Status fmi3GetString (fmi3Component c, const fmi3ValueReference vr[], size_t nvr, fmi3String value[], size_t nValues) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3GetString", MASK_fmi3GetString)) - return fmi3Error; - - if (nvr>0 && nullPointer(comp, "fmi3GetString", "vr[]", vr)) - return fmi3Error; - - if (nvr>0 && nullPointer(comp, "fmi3GetString", "value[]", value)) - return fmi3Error; - - if (nvr > 0 && comp->isDirtyValues) { - calculateValues(comp); - comp->isDirtyValues = false; - } - - GET_VARIABLES(String) -} - -fmi3Status fmi3SetFloat64 (fmi3Component c, const fmi3ValueReference vr[], size_t nvr, const fmi3Float64 value[], size_t nValues) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3SetReal", MASK_fmi3SetReal)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3SetReal", "vr[]", vr)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3SetReal", "value[]", value)) - return fmi3Error; - - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3SetReal: nvr = %d", nvr) - - SET_VARIABLES(Float64) -} - -fmi3Status fmi3SetInt32(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, const fmi3Int32 value[], size_t nValues) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3SetInteger", MASK_fmi3SetInteger)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3SetInteger", "vr[]", vr)) - return fmi3Error; - - if (nvr > 0 && nullPointer(comp, "fmi3SetInteger", "value[]", value)) - return fmi3Error; - - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3SetInteger: nvr = %d", nvr) - - SET_VARIABLES(Int32) -} - -fmi3Status fmi3SetBoolean(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, const fmi3Boolean value[], size_t nValues) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3SetBoolean", MASK_fmi3SetBoolean)) - return fmi3Error; - - if (nvr>0 && nullPointer(comp, "fmi3SetBoolean", "vr[]", vr)) - return fmi3Error; - - if (nvr>0 && nullPointer(comp, "fmi3SetBoolean", "value[]", value)) - return fmi3Error; - - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3SetBoolean: nvr = %d", nvr) - - SET_BOOLEAN_VARIABLES -} - -fmi3Status fmi3SetString (fmi3Component c, const fmi3ValueReference vr[], size_t nvr, const fmi3String value[], size_t nValues) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3SetString", MASK_fmi3SetString)) - return fmi3Error; - - if (nvr>0 && nullPointer(comp, "fmi3SetString", "vr[]", vr)) - return fmi3Error; - - if (nvr>0 && nullPointer(comp, "fmi3SetString", "value[]", value)) - return fmi3Error; - - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3SetString: nvr = %d", nvr) - - SET_VARIABLES(String) -} - -fmi3Status fmi3GetFMUstate (fmi3Component c, fmi3FMUstate* FMUstate) { - return unsupportedFunction(c, "fmi3GetFMUstate", MASK_fmi3GetFMUstate); -} -fmi3Status fmi3SetFMUstate (fmi3Component c, fmi3FMUstate FMUstate) { - return unsupportedFunction(c, "fmi3SetFMUstate", MASK_fmi3SetFMUstate); -} -fmi3Status fmi3FreeFMUstate(fmi3Component c, fmi3FMUstate* FMUstate) { - return unsupportedFunction(c, "fmi3FreeFMUstate", MASK_fmi3FreeFMUstate); -} -fmi3Status fmi3SerializedFMUstateSize(fmi3Component c, fmi3FMUstate FMUstate, size_t *size) { - return unsupportedFunction(c, "fmi3SerializedFMUstateSize", MASK_fmi3SerializedFMUstateSize); -} -fmi3Status fmi3SerializeFMUstate (fmi3Component c, fmi3FMUstate FMUstate, fmi3Byte serializedState[], size_t size) { - return unsupportedFunction(c, "fmi3SerializeFMUstate", MASK_fmi3SerializeFMUstate); -} -fmi3Status fmi3DeSerializeFMUstate (fmi3Component c, const fmi3Byte serializedState[], size_t size, - fmi3FMUstate* FMUstate) { - return unsupportedFunction(c, "fmi3DeSerializeFMUstate", MASK_fmi3DeSerializeFMUstate); -} - -//fmi3Status fmi3GetDirectionalDerivative(fmi3Component c, const fmi3ValueReference vUnknown_ref[], size_t nUnknown, -// const fmi3ValueReference vKnown_ref[] , size_t nKnown, -// const fmi3Float64 dvKnown[], fmi3Float64 dvUnknown[]) { -// return unsupportedFunction(c, "fmi3GetDirectionalDerivative", MASK_fmi3GetDirectionalDerivative); -//} - -// --------------------------------------------------------------------------- -// Functions for FMI for Co-Simulation -// --------------------------------------------------------------------------- -/* Simulating the slave */ -//fmi3Status fmi3SetRealInputDerivatives(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, -// const fmi3Integer order[], const fmi3Float64 value[]) { -// ModelInstance *comp = (ModelInstance *)c; -// if (invalidState(comp, "fmi3SetRealInputDerivatives", MASK_fmi3SetRealInputDerivatives)) { -// return fmi3Error; -// } -// FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3SetRealInputDerivatives: nvr= %d", nvr) -// FILTERED_LOG(comp, fmi3Error, LOG_ERROR, "fmi3SetRealInputDerivatives: ignoring function call." -// " This model cannot interpolate inputs: canInterpolateInputs=\"fmi3False\"") -// return fmi3Error; -//} - -//fmi3Status fmi3GetRealOutputDerivatives(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, -// const fmi3Integer order[], fmi3Float64 value[]) { -// int i; -// ModelInstance *comp = (ModelInstance *)c; -// if (invalidState(comp, "fmi3GetRealOutputDerivatives", MASK_fmi3GetRealOutputDerivatives)) -// return fmi3Error; -// FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3GetRealOutputDerivatives: nvr= %d", nvr) -// FILTERED_LOG(comp, fmi3Error, LOG_ERROR,"fmi3GetRealOutputDerivatives: ignoring function call." -// " This model cannot compute derivatives of outputs: MaxOutputDerivativeOrder=\"0\"") -// for (i = 0; i < nvr; i++) value[i] = 0; -// return fmi3Error; -//} - -fmi3Status fmi3CancelStep(fmi3Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3CancelStep", MASK_fmi3CancelStep)) { - // always fmi3CancelStep is invalid, because model is never in modelStepInProgress state. - return fmi3Error; - } - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3CancelStep") - FILTERED_LOG(comp, fmi3Error, LOG_ERROR,"fmi3CancelStep: Can be called when fmi3DoStep returned fmi3Pending." - " This is not the case."); - // comp->state = modelStepCanceled; - return fmi3Error; -} - -fmi3Status fmi3DoStep(fmi3Component c, fmi3Float64 currentCommunicationPoint, - fmi3Float64 communicationStepSize, fmi3Boolean noSetFMUStatePriorToCurrentPoint) { - ModelInstance *comp = (ModelInstance *)c; - - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3DoStep: " - "currentCommunicationPoint = %g, " - "communicationStepSize = %g, " - "noSetFMUStatePriorToCurrentPoint = fmi3%s", - currentCommunicationPoint, communicationStepSize, noSetFMUStatePriorToCurrentPoint ? "True" : "False") - - if (communicationStepSize <= 0) { - FILTERED_LOG(comp, fmi3Error, LOG_ERROR, - "fmi3DoStep: communication step size must be > 0. Fount %g.", communicationStepSize) - comp->state = modelError; - return fmi3Error; - } - - return doStep(comp, currentCommunicationPoint, currentCommunicationPoint + communicationStepSize); -} - -// --------------------------------------------------------------------------- -// Functions for fmi3 for Model Exchange -// --------------------------------------------------------------------------- -/* Enter and exit the different modes */ -fmi3Status fmi3EnterEventMode(fmi3Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3EnterEventMode", MASK_fmi3EnterEventMode)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3EnterEventMode") - - comp->state = modelEventMode; - comp->isNewEventIteration = fmi3True; - return fmi3OK; -} - -fmi3Status fmi3NewDiscreteStates(fmi3Component c, fmi3EventInfo *eventInfo) { - ModelInstance *comp = (ModelInstance *)c; - int timeEvent = 0; - if (invalidState(comp, "fmi3NewDiscreteStates", MASK_fmi3NewDiscreteStates)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3NewDiscreteStates") - - comp->newDiscreteStatesNeeded = fmi3False; - comp->terminateSimulation = fmi3False; - comp->nominalsOfContinuousStatesChanged = fmi3False; - comp->valuesOfContinuousStatesChanged = fmi3False; - - if (comp->nextEventTimeDefined && comp->nextEventTime <= comp->time) { - timeEvent = 1; - } - - eventUpdate(comp); - - comp->isNewEventIteration = false; - - // copy internal eventInfo of component to output eventInfo - eventInfo->newDiscreteStatesNeeded = comp->newDiscreteStatesNeeded; - eventInfo->terminateSimulation = comp->terminateSimulation; - eventInfo->nominalsOfContinuousStatesChanged = comp->nominalsOfContinuousStatesChanged; - eventInfo->valuesOfContinuousStatesChanged = comp->valuesOfContinuousStatesChanged; - eventInfo->nextEventTimeDefined = comp->nextEventTimeDefined; - eventInfo->nextEventTime = comp->nextEventTime; - - return fmi3OK; -} - -fmi3Status fmi3EnterContinuousTimeMode(fmi3Component c) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3EnterContinuousTimeMode", MASK_fmi3EnterContinuousTimeMode)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL,"fmi3EnterContinuousTimeMode") - - comp->state = modelContinuousTimeMode; - return fmi3OK; -} - -fmi3Status fmi3CompletedIntegratorStep(fmi3Component c, fmi3Boolean noSetFMUStatePriorToCurrentPoint, - fmi3Boolean *enterEventMode, fmi3Boolean *terminateSimulation) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3CompletedIntegratorStep", MASK_fmi3CompletedIntegratorStep)) - return fmi3Error; - if (nullPointer(comp, "fmi3CompletedIntegratorStep", "enterEventMode", enterEventMode)) - return fmi3Error; - if (nullPointer(comp, "fmi3CompletedIntegratorStep", "terminateSimulation", terminateSimulation)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL,"fmi3CompletedIntegratorStep") - *enterEventMode = fmi3False; - *terminateSimulation = fmi3False; - return fmi3OK; -} - -/* Providing independent variables and re-initialization of caching */ -fmi3Status fmi3SetTime(fmi3Component c, fmi3Float64 time) { - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3SetTime", MASK_fmi3SetTime)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3SetTime: time=%.16g", time) - comp->time = time; - return fmi3OK; -} - -fmi3Status fmi3SetContinuousStates(fmi3Component c, const fmi3Float64 x[], size_t nx){ - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3SetContinuousStates", MASK_fmi3SetContinuousStates)) - return fmi3Error; - - if (invalidNumber(comp, "fmi3SetContinuousStates", "nx", nx, NUMBER_OF_STATES)) - return fmi3Error; - - if (nullPointer(comp, "fmi3SetContinuousStates", "x[]", x)) - return fmi3Error; - - setContinuousStates(comp, x, nx); - - return fmi3OK; -} - -/* Evaluation of the model equations */ -fmi3Status fmi3GetDerivatives(fmi3Component c, fmi3Float64 derivatives[], size_t nx) { - - ModelInstance* comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3GetDerivatives", MASK_fmi3GetDerivatives)) - return fmi3Error; - - if (invalidNumber(comp, "fmi3GetDerivatives", "nx", nx, NUMBER_OF_STATES)) - return fmi3Error; - - if (nullPointer(comp, "fmi3GetDerivatives", "derivatives[]", derivatives)) - return fmi3Error; - - getDerivatives(comp, derivatives, nx); - - return fmi3OK; -} - -fmi3Status fmi3GetEventIndicators(fmi3Component c, fmi3Float64 eventIndicators[], size_t ni) { - -#if NUMBER_OF_EVENT_INDICATORS > 0 - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3GetEventIndicators", MASK_fmi3GetEventIndicators)) - return fmi3Error; - - if (invalidNumber(comp, "fmi3GetEventIndicators", "ni", ni, NUMBER_OF_EVENT_INDICATORS)) - return fmi3Error; - - getEventIndicators(comp, eventIndicators, ni); -#else - if (ni > 0) return fmi3Error; -#endif - return fmi3OK; -} - -fmi3Status fmi3GetContinuousStates(fmi3Component c, fmi3Float64 states[], size_t nx) { - - ModelInstance *comp = (ModelInstance *)c; - - if (invalidState(comp, "fmi3GetContinuousStates", MASK_fmi3GetContinuousStates)) - return fmi3Error; - - if (invalidNumber(comp, "fmi3GetContinuousStates", "nx", nx, NUMBER_OF_STATES)) - return fmi3Error; - - if (nullPointer(comp, "fmi3GetContinuousStates", "states[]", states)) - return fmi3Error; - - getContinuousStates(comp, states, nx); - - return fmi3OK; -} - -fmi3Status fmi3GetNominalsOfContinuousStates(fmi3Component c, fmi3Float64 x_nominal[], size_t nx) { - int i; - ModelInstance *comp = (ModelInstance *)c; - if (invalidState(comp, "fmi3GetNominalsOfContinuousStates", MASK_fmi3GetNominalsOfContinuousStates)) - return fmi3Error; - if (invalidNumber(comp, "fmi3GetNominalContinuousStates", "nx", nx, NUMBER_OF_STATES)) - return fmi3Error; - if (nullPointer(comp, "fmi3GetNominalContinuousStates", "x_nominal[]", x_nominal)) - return fmi3Error; - FILTERED_LOG(comp, fmi3OK, LOG_FMI_CALL, "fmi3GetNominalContinuousStates: x_nominal[0..%d] = 1.0", nx-1) - for (i = 0; i < nx; i++) - x_nominal[i] = 1; - return fmi3OK; -} +/**************************************************************** + * Copyright (c) Dassault Systemes. All rights reserved. * + * This file is part of the Test-FMUs. See LICENSE.txt in the * + * project root for license information. * + ****************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "model.h" +#include "slave.h" + + +// C-code FMUs have functions names prefixed with MODEL_IDENTIFIER_. +// Define DISABLE_PREFIX to build a binary FMU. +#ifndef DISABLE_PREFIX +#define pasteA(a,b) a ## b +#define pasteB(a,b) pasteA(a,b) +#define fmi3_FUNCTION_PREFIX pasteB(MODEL_IDENTIFIER, _) +#endif +#include "fmi3Functions.h" + +#ifndef max +#define max(a,b) ((a)>(b) ? (a) : (b)) +#endif + +#ifndef DT_EVENT_DETECT +#define DT_EVENT_DETECT 1e-10 +#endif + +// --------------------------------------------------------------------------- +// Function calls allowed state masks for both Model-exchange and Co-simulation +// --------------------------------------------------------------------------- +#define MASK_fmi3GetTypesPlatform (modelStartAndEnd | modelInstantiated | modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepInProgress | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi3GetVersion MASK_fmi3GetTypesPlatform +#define MASK_fmi3SetDebugLogging (modelInstantiated | modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepInProgress | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi3Instantiate (modelStartAndEnd) +#define MASK_fmi3FreeInstance (modelInstantiated | modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi3SetupExperiment modelInstantiated +#define MASK_fmi3EnterInitializationMode modelInstantiated +#define MASK_fmi3ExitInitializationMode modelInitializationMode +#define MASK_fmi3Terminate (modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepFailed) +#define MASK_fmi3Reset MASK_fmi3FreeInstance +#define MASK_fmi3GetReal (modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi3GetInteger MASK_fmi3GetReal +#define MASK_fmi3GetBoolean MASK_fmi3GetReal +#define MASK_fmi3GetString MASK_fmi3GetReal +#define MASK_fmi3SetReal (modelInstantiated | modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete) +#define MASK_fmi3SetInteger (modelInstantiated | modelInitializationMode \ +| modelEventMode \ +| modelStepComplete) +#define MASK_fmi3SetBoolean MASK_fmi3SetInteger +#define MASK_fmi3SetString MASK_fmi3SetInteger +#define MASK_fmi3GetFMUstate MASK_fmi3FreeInstance +#define MASK_fmi3SetFMUstate MASK_fmi3FreeInstance +#define MASK_fmi3FreeFMUstate MASK_fmi3FreeInstance +#define MASK_fmi3SerializedFMUstateSize MASK_fmi3FreeInstance +#define MASK_fmi3SerializeFMUstate MASK_fmi3FreeInstance +#define MASK_fmi3DeSerializeFMUstate MASK_fmi3FreeInstance +#define MASK_fmi3GetDirectionalDerivative (modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelStepComplete | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) + +// --------------------------------------------------------------------------- +// Function calls allowed state masks for Model-exchange +// --------------------------------------------------------------------------- +#define MASK_fmi3EnterEventMode (modelEventMode | modelContinuousTimeMode) +#define MASK_fmi3NewDiscreteStates modelEventMode +#define MASK_fmi3EnterContinuousTimeMode modelEventMode +#define MASK_fmi3CompletedIntegratorStep modelContinuousTimeMode +#define MASK_fmi3SetTime (modelEventMode | modelContinuousTimeMode) +#define MASK_fmi3SetContinuousStates modelContinuousTimeMode +#define MASK_fmi3GetEventIndicators (modelInitializationMode \ +| modelEventMode | modelContinuousTimeMode \ +| modelTerminated | modelError) +#define MASK_fmi3GetContinuousStates MASK_fmi3GetEventIndicators +#define MASK_fmi3GetDerivatives (modelEventMode | modelContinuousTimeMode \ +| modelTerminated | modelError) +#define MASK_fmi3GetNominalsOfContinuousStates ( modelInstantiated \ +| modelEventMode | modelContinuousTimeMode \ +| modelTerminated | modelError) + +// --------------------------------------------------------------------------- +// Function calls allowed state masks for Co-simulation +// --------------------------------------------------------------------------- +#define MASK_fmi3SetRealInputDerivatives (modelInstantiated | modelInitializationMode \ +| modelStepComplete) +#define MASK_fmi3GetRealOutputDerivatives (modelStepComplete | modelStepFailed | modelStepCanceled \ +| modelTerminated | modelError) +#define MASK_fmi3DoStep modelStepComplete +#define MASK_fmi3CancelStep modelStepInProgress +#define MASK_fmi3GetStatus (modelStepComplete | modelStepInProgress | modelStepFailed \ +| modelTerminated) +#define MASK_fmi3GetRealStatus MASK_fmi3GetStatus +#define MASK_fmi3GetIntegerStatus MASK_fmi3GetStatus +#define MASK_fmi3GetBooleanStatus MASK_fmi3GetStatus +#define MASK_fmi3GetStringStatus MASK_fmi3GetStatus + +// --------------------------------------------------------------------------- +// Private helpers used below to validate function arguments +// --------------------------------------------------------------------------- + +static fmi3Status unsupportedFunction(fmi3Component c, const char *fName, int statesExpected) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, fName, statesExpected)) + return fmi3Error; + logError(comp, "%s: Function not implemented.", fName); + return fmi3Error; +} + +// --------------------------------------------------------------------------- +// FMI functions +// --------------------------------------------------------------------------- + +fmi3Component fmi3Instantiate(fmi3String instanceName, fmi3Type fmuType, fmi3String fmuGUID, + fmi3String fmuResourceLocation, const fmi3CallbackFunctions *functions, + fmi3Boolean visible, fmi3Boolean loggingOn) { + + return createModelInstance( + (loggerType)functions->logger, + (allocateMemoryType)functions->allocateMemory, + (freeMemoryType)functions->freeMemory, + functions->componentEnvironment, + instanceName, + fmuGUID, + fmuResourceLocation, + loggingOn, + fmuType + ); +} + +fmi3Status fmi3SetupExperiment(fmi3Component c, fmi3Boolean toleranceDefined, fmi3Float64 tolerance, + fmi3Float64 startTime, fmi3Boolean stopTimeDefined, fmi3Float64 stopTime) { + + // ignore arguments: stopTimeDefined, stopTime + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3SetupExperiment", MASK_fmi3SetupExperiment)) + return fmi3Error; + + comp->time = startTime; + + return fmi3OK; +} + +fmi3Status fmi3EnterInitializationMode(fmi3Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3EnterInitializationMode", MASK_fmi3EnterInitializationMode)) + return fmi3Error; + comp->state = modelInitializationMode; + return fmi3OK; +} + +fmi3Status fmi3ExitInitializationMode(fmi3Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3ExitInitializationMode", MASK_fmi3ExitInitializationMode)) + return fmi3Error; + + // if values were set and no fmi3GetXXX triggered update before, + // ensure calculated values are updated now + if (comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + if (comp->type == fmi3ModelExchange) { + comp->state = modelEventMode; + comp->isNewEventIteration = fmi3True; + } else { + comp->state = modelStepComplete; + } + + return fmi3OK; +} + +fmi3Status fmi3Terminate(fmi3Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3Terminate", MASK_fmi3Terminate)) + return fmi3Error; + comp->state = modelTerminated; + return fmi3OK; +} + +fmi3Status fmi3Reset(fmi3Component c) { + ModelInstance* comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3Reset", MASK_fmi3Reset)) + return fmi3Error; + comp->state = modelInstantiated; + setStartValues(comp); + comp->isDirtyValues = true; + return fmi3OK; +} + +void fmi3FreeInstance(fmi3Component c) { + + ModelInstance *comp = (ModelInstance *)c; + + if (!comp) return; + + if (invalidState(comp, "fmi3FreeInstance", MASK_fmi3FreeInstance)) + return; + + freeModelInstance(comp); +} + +// --------------------------------------------------------------------------- +// FMI functions: class methods not depending of a specific model instance +// --------------------------------------------------------------------------- + +const char* fmi3GetVersion() { + return fmi3Version; +} + +// --------------------------------------------------------------------------- +// FMI functions: logging control, setters and getters for Real, Integer, +// Boolean, String +// --------------------------------------------------------------------------- + +fmi3Status fmi3SetDebugLogging(fmi3Component c, fmi3Boolean loggingOn, size_t nCategories, const fmi3String categories[]) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3SetDebugLogging", MASK_fmi3SetDebugLogging)) + return fmi3Error; + + return setDebugLogging(comp, loggingOn, nCategories, categories); +} + +fmi3Status fmi3GetFloat64(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, fmi3Float64 value[], size_t nValues) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3GetReal", MASK_fmi3GetReal)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3GetReal", "vr[]", vr)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3GetReal", "value[]", value)) + return fmi3Error; + + if (nvr > 0 && comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + GET_VARIABLES(Float64) +} + +fmi3Status fmi3GetInt32(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, fmi3Int32 value[], size_t nValues) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3GetInteger", MASK_fmi3GetInteger)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3GetInteger", "vr[]", vr)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3GetInteger", "value[]", value)) + return fmi3Error; + + if (nvr > 0 && comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + GET_VARIABLES(Int32) +} + +fmi3Status fmi3GetBoolean(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, fmi3Boolean value[], size_t nValues) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3GetBoolean", MASK_fmi3GetBoolean)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3GetBoolean", "vr[]", vr)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3GetBoolean", "value[]", value)) + return fmi3Error; + + if (nvr > 0 && comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + GET_BOOLEAN_VARIABLES +} + +fmi3Status fmi3GetString(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, fmi3String value[], size_t nValues) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3GetString", MASK_fmi3GetString)) + return fmi3Error; + + if (nvr>0 && nullPointer(comp, "fmi3GetString", "vr[]", vr)) + return fmi3Error; + + if (nvr>0 && nullPointer(comp, "fmi3GetString", "value[]", value)) + return fmi3Error; + + if (nvr > 0 && comp->isDirtyValues) { + calculateValues(comp); + comp->isDirtyValues = false; + } + + GET_VARIABLES(String) +} + +fmi3Status fmi3SetFloat64(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, const fmi3Float64 value[], size_t nValues) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3SetReal", MASK_fmi3SetReal)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3SetReal", "vr[]", vr)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3SetReal", "value[]", value)) + return fmi3Error; + + SET_VARIABLES(Float64) +} + +fmi3Status fmi3SetInt32(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, const fmi3Int32 value[], size_t nValues) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3SetInteger", MASK_fmi3SetInteger)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3SetInteger", "vr[]", vr)) + return fmi3Error; + + if (nvr > 0 && nullPointer(comp, "fmi3SetInteger", "value[]", value)) + return fmi3Error; + + SET_VARIABLES(Int32) +} + +fmi3Status fmi3SetBoolean(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, const fmi3Boolean value[], size_t nValues) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3SetBoolean", MASK_fmi3SetBoolean)) + return fmi3Error; + + if (nvr>0 && nullPointer(comp, "fmi3SetBoolean", "vr[]", vr)) + return fmi3Error; + + if (nvr>0 && nullPointer(comp, "fmi3SetBoolean", "value[]", value)) + return fmi3Error; + + SET_BOOLEAN_VARIABLES +} + +fmi3Status fmi3SetString(fmi3Component c, const fmi3ValueReference vr[], size_t nvr, const fmi3String value[], size_t nValues) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3SetString", MASK_fmi3SetString)) + return fmi3Error; + + if (nvr>0 && nullPointer(comp, "fmi3SetString", "vr[]", vr)) + return fmi3Error; + + if (nvr>0 && nullPointer(comp, "fmi3SetString", "value[]", value)) + return fmi3Error; + + SET_VARIABLES(String) +} + +fmi3Status fmi3GetFMUstate(fmi3Component c, fmi3FMUstate* FMUstate) { + return unsupportedFunction(c, "fmi3GetFMUstate", MASK_fmi3GetFMUstate); +} +fmi3Status fmi3SetFMUstate(fmi3Component c, fmi3FMUstate FMUstate) { + return unsupportedFunction(c, "fmi3SetFMUstate", MASK_fmi3SetFMUstate); +} +fmi3Status fmi3FreeFMUstate(fmi3Component c, fmi3FMUstate* FMUstate) { + return unsupportedFunction(c, "fmi3FreeFMUstate", MASK_fmi3FreeFMUstate); +} +fmi3Status fmi3SerializedFMUstateSize(fmi3Component c, fmi3FMUstate FMUstate, size_t *size) { + return unsupportedFunction(c, "fmi3SerializedFMUstateSize", MASK_fmi3SerializedFMUstateSize); +} +fmi3Status fmi3SerializeFMUstate(fmi3Component c, fmi3FMUstate FMUstate, fmi3Byte serializedState[], size_t size) { + return unsupportedFunction(c, "fmi3SerializeFMUstate", MASK_fmi3SerializeFMUstate); +} +fmi3Status fmi3DeSerializeFMUstate (fmi3Component c, const fmi3Byte serializedState[], size_t size, + fmi3FMUstate* FMUstate) { + return unsupportedFunction(c, "fmi3DeSerializeFMUstate", MASK_fmi3DeSerializeFMUstate); +} + +// --------------------------------------------------------------------------- +// Functions for FMI for Co-Simulation +// --------------------------------------------------------------------------- + +fmi3Status fmi3CancelStep(fmi3Component c) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3CancelStep", MASK_fmi3CancelStep)) { + // always fmi3CancelStep is invalid, because model is never in modelStepInProgress state. + return fmi3Error; + } + + logError(comp, "fmi3CancelStep: Can be called when fmi3DoStep returned fmi3Pending." + " This is not the case."); + + return fmi3Error; +} + +fmi3Status fmi3DoStep(fmi3Component c, fmi3Float64 currentCommunicationPoint, + fmi3Float64 communicationStepSize, fmi3Boolean noSetFMUStatePriorToCurrentPoint) { + + ModelInstance *comp = (ModelInstance *)c; + + if (communicationStepSize <= 0) { + logError(comp, "fmi3DoStep: communication step size must be > 0 but was %g.", communicationStepSize); + comp->state = modelError; + return fmi3Error; + } + + return doStep(comp, currentCommunicationPoint, currentCommunicationPoint + communicationStepSize); +} + +// --------------------------------------------------------------------------- +// Functions for fmi3 for Model Exchange +// --------------------------------------------------------------------------- +/* Enter and exit the different modes */ +fmi3Status fmi3EnterEventMode(fmi3Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3EnterEventMode", MASK_fmi3EnterEventMode)) + return fmi3Error; + comp->state = modelEventMode; + comp->isNewEventIteration = fmi3True; + return fmi3OK; +} + +fmi3Status fmi3NewDiscreteStates(fmi3Component c, fmi3EventInfo *eventInfo) { + ModelInstance *comp = (ModelInstance *)c; + int timeEvent = 0; + if (invalidState(comp, "fmi3NewDiscreteStates", MASK_fmi3NewDiscreteStates)) + return fmi3Error; + + comp->newDiscreteStatesNeeded = fmi3False; + comp->terminateSimulation = fmi3False; + comp->nominalsOfContinuousStatesChanged = fmi3False; + comp->valuesOfContinuousStatesChanged = fmi3False; + + if (comp->nextEventTimeDefined && comp->nextEventTime <= comp->time) { + timeEvent = 1; + } + + eventUpdate(comp); + + comp->isNewEventIteration = false; + + // copy internal eventInfo of component to output eventInfo + eventInfo->newDiscreteStatesNeeded = comp->newDiscreteStatesNeeded; + eventInfo->terminateSimulation = comp->terminateSimulation; + eventInfo->nominalsOfContinuousStatesChanged = comp->nominalsOfContinuousStatesChanged; + eventInfo->valuesOfContinuousStatesChanged = comp->valuesOfContinuousStatesChanged; + eventInfo->nextEventTimeDefined = comp->nextEventTimeDefined; + eventInfo->nextEventTime = comp->nextEventTime; + + return fmi3OK; +} + +fmi3Status fmi3EnterContinuousTimeMode(fmi3Component c) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3EnterContinuousTimeMode", MASK_fmi3EnterContinuousTimeMode)) + return fmi3Error; + comp->state = modelContinuousTimeMode; + return fmi3OK; +} + +fmi3Status fmi3CompletedIntegratorStep(fmi3Component c, fmi3Boolean noSetFMUStatePriorToCurrentPoint, + fmi3Boolean *enterEventMode, fmi3Boolean *terminateSimulation) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3CompletedIntegratorStep", MASK_fmi3CompletedIntegratorStep)) + return fmi3Error; + if (nullPointer(comp, "fmi3CompletedIntegratorStep", "enterEventMode", enterEventMode)) + return fmi3Error; + if (nullPointer(comp, "fmi3CompletedIntegratorStep", "terminateSimulation", terminateSimulation)) + return fmi3Error; + *enterEventMode = fmi3False; + *terminateSimulation = fmi3False; + return fmi3OK; +} + +/* Providing independent variables and re-initialization of caching */ +fmi3Status fmi3SetTime(fmi3Component c, fmi3Float64 time) { + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3SetTime", MASK_fmi3SetTime)) + return fmi3Error; + comp->time = time; + return fmi3OK; +} + +fmi3Status fmi3SetContinuousStates(fmi3Component c, const fmi3Float64 x[], size_t nx){ + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3SetContinuousStates", MASK_fmi3SetContinuousStates)) + return fmi3Error; + + if (invalidNumber(comp, "fmi3SetContinuousStates", "nx", nx, NUMBER_OF_STATES)) + return fmi3Error; + + if (nullPointer(comp, "fmi3SetContinuousStates", "x[]", x)) + return fmi3Error; + + setContinuousStates(comp, x, nx); + + return fmi3OK; +} + +/* Evaluation of the model equations */ +fmi3Status fmi3GetDerivatives(fmi3Component c, fmi3Float64 derivatives[], size_t nx) { + + ModelInstance* comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3GetDerivatives", MASK_fmi3GetDerivatives)) + return fmi3Error; + + if (invalidNumber(comp, "fmi3GetDerivatives", "nx", nx, NUMBER_OF_STATES)) + return fmi3Error; + + if (nullPointer(comp, "fmi3GetDerivatives", "derivatives[]", derivatives)) + return fmi3Error; + + getDerivatives(comp, derivatives, nx); + + return fmi3OK; +} + +fmi3Status fmi3GetEventIndicators(fmi3Component c, fmi3Float64 eventIndicators[], size_t ni) { + +#if NUMBER_OF_EVENT_INDICATORS > 0 + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3GetEventIndicators", MASK_fmi3GetEventIndicators)) + return fmi3Error; + + if (invalidNumber(comp, "fmi3GetEventIndicators", "ni", ni, NUMBER_OF_EVENT_INDICATORS)) + return fmi3Error; + + getEventIndicators(comp, eventIndicators, ni); +#else + if (ni > 0) return fmi3Error; +#endif + return fmi3OK; +} + +fmi3Status fmi3GetContinuousStates(fmi3Component c, fmi3Float64 states[], size_t nx) { + + ModelInstance *comp = (ModelInstance *)c; + + if (invalidState(comp, "fmi3GetContinuousStates", MASK_fmi3GetContinuousStates)) + return fmi3Error; + + if (invalidNumber(comp, "fmi3GetContinuousStates", "nx", nx, NUMBER_OF_STATES)) + return fmi3Error; + + if (nullPointer(comp, "fmi3GetContinuousStates", "states[]", states)) + return fmi3Error; + + getContinuousStates(comp, states, nx); + + return fmi3OK; +} + +fmi3Status fmi3GetNominalsOfContinuousStates(fmi3Component c, fmi3Float64 x_nominal[], size_t nx) { + int i; + ModelInstance *comp = (ModelInstance *)c; + if (invalidState(comp, "fmi3GetNominalsOfContinuousStates", MASK_fmi3GetNominalsOfContinuousStates)) + return fmi3Error; + if (invalidNumber(comp, "fmi3GetNominalContinuousStates", "nx", nx, NUMBER_OF_STATES)) + return fmi3Error; + if (nullPointer(comp, "fmi3GetNominalContinuousStates", "x_nominal[]", x_nominal)) + return fmi3Error; + for (i = 0; i < nx; i++) + x_nominal[i] = 1; + return fmi3OK; +} diff --git a/src/slave.c b/src/slave.c index 4053821..7519468 100644 --- a/src/slave.c +++ b/src/slave.c @@ -4,10 +4,244 @@ * project root for license information. * ****************************************************************/ +#include +#include +#include #include "config.h" #include "slave.h" -#include "solver.h" +ModelInstance *createModelInstance( + loggerType cbLogger, + allocateMemoryType cbAllocateMemory, + freeMemoryType cbFreeMemory, + void *componentEnvironment, + const char *instanceName, + const char *GUID, + const char *resourceLocation, + bool loggingOn, + InterfaceType interfaceType) { + + ModelInstance *comp = NULL; + + if (!cbLogger) { + return NULL; + } + + if (!cbAllocateMemory || !cbFreeMemory) { + cbLogger(componentEnvironment, instanceName, Error, "error", "Missing callback function."); + return NULL; + } + + if (!instanceName || strlen(instanceName) == 0) { + cbLogger(componentEnvironment, "?", Error, "error", "Missing instance name."); + return NULL; + } + + if (!GUID || strlen(GUID) == 0) { + cbLogger(componentEnvironment, instanceName, Error, "error", "Missing GUID."); + return NULL; + } + + if (strcmp(GUID, MODEL_GUID)) { + cbLogger(componentEnvironment, instanceName, Error, "error", "Wrong GUID."); + return NULL; + } + +#if FMI_VERSION < 3 + comp = (ModelInstance *)cbAllocateMemory(1, sizeof(ModelInstance)); +#else + comp = (ModelInstance *)cbAllocateMemory(componentEnvironment, 1, sizeof(ModelInstance)); +#endif + + if (comp) { + + // set the callbacks + comp->componentEnvironment = componentEnvironment; + comp->logger = cbLogger; + comp->allocateMemory = cbAllocateMemory; + comp->freeMemory = cbFreeMemory; + + comp->instanceName = (char *)allocateMemory(comp, 1 + strlen(instanceName), sizeof(char)); + + // resourceLocation is NULL for FMI 1.0 ME + if (resourceLocation) { + comp->resourceLocation = (char *)allocateMemory(comp, 1 + strlen(resourceLocation), sizeof(char)); + strcpy((char *)comp->resourceLocation, (char *)resourceLocation); + } else { + comp->resourceLocation = NULL; + } + + comp->modelData = (ModelData *)allocateMemory(comp, 1, sizeof(ModelData)); + + comp->logEvents = loggingOn; + comp->logErrors = true; // always log errors + } + + if (!comp || !comp->modelData || !comp->instanceName) { + logError(comp, "Out of memory."); + return NULL; + } + + comp->time = 0; // overwrite in fmi*SetupExperiment, fmi*SetTime + strcpy((char *)comp->instanceName, (char *)instanceName); + comp->type = interfaceType; + + comp->state = modelInstantiated; + comp->isNewEventIteration = false; + + comp->newDiscreteStatesNeeded = false; + comp->terminateSimulation = false; + comp->nominalsOfContinuousStatesChanged = false; + comp->valuesOfContinuousStatesChanged = false; + comp->nextEventTimeDefined = false; + comp->nextEventTime = 0; + + setStartValues(comp); // to be implemented by the includer of this file + comp->isDirtyValues = true; // because we just called setStartValues + +#if NUMBER_OF_EVENT_INDICATORS > 0 + comp->z = allocateMemory(comp, sizeof(double), NUMBER_OF_EVENT_INDICATORS); + comp->prez = allocateMemory(comp, sizeof(double), NUMBER_OF_EVENT_INDICATORS); +#else + comp->z = NULL; + comp->prez = NULL; +#endif + + return comp; +} + +void freeModelInstance(ModelInstance *comp) { + freeMemory(comp, (void *)comp->instanceName); + freeMemory(comp, (void *)comp->z); + freeMemory(comp, (void *)comp->prez); + freeMemory(comp, comp); +} + +void *allocateMemory(ModelInstance *comp, size_t num, size_t size) { +#if FMI_VERSION > 2 + return comp->allocateMemory(comp->componentEnvironment, num, size); +#else + return comp->allocateMemory(num, size); +#endif +} + +void freeMemory(ModelInstance *comp, void *obj) { +#if FMI_VERSION > 2 + comp->freeMemory(comp->componentEnvironment, obj); +#else + comp->freeMemory(obj); +#endif +} + +const char *duplicateString(ModelInstance *comp, const char *str1) { + size_t len = strlen(str1); + char *str2 = allocateMemory(comp, len + 1, sizeof(char)); + strncpy(str2, str1, len + 1); + return str2; +} + +bool invalidNumber(ModelInstance *comp, const char *f, const char *arg, size_t actual, size_t expected) { + + if (actual != expected) { + comp->state = modelError; + logError(comp, "%s: Invalid argument %s = %d. Expected %d.", f, arg, actual, expected); + return true; + } + + return false; +} + +bool invalidState(ModelInstance *comp, const char *f, int statesExpected) { + + if (!comp) { + return true; + } + + if (!(comp->state & statesExpected)) { + comp->state = modelError; + logError(comp, "%s: Illegal call sequence.", f); + return true; + } + + return false; +} + +bool nullPointer(ModelInstance* comp, const char *f, const char *arg, const void *p) { + + if (!p) { + comp->state = modelError; + logError(comp, "%s: Invalid argument %s = NULL.", f, arg); + return true; + } + + return false; +} + +Status setDebugLogging(ModelInstance *comp, bool loggingOn, size_t nCategories, const char * const categories[]) { + + if (loggingOn) { + for (size_t i = 0; i < nCategories; i++) { + if (categories[i] == NULL) { + logError(comp, "Log category[%d] must not be NULL", i); + return Error; + } else if (strcmp(categories[i], "logEvents") == 0) { + comp->logEvents = true; + } else if (strcmp(categories[i], "logStatusError") == 0) { + comp->logErrors = true; + } else { + logError(comp, "Log category[%d] must be one of logEvents or logStatusError but was %s", i, categories[i]); + return Error; + } + } + } else { + // disable logging + comp->logEvents = false; + comp->logErrors = false; + } + + return OK; +} + +static void logMessage(ModelInstance *comp, int status, const char *category, const char *message, va_list args) { + + va_list args1; + size_t len = 0; + char *buf = ""; + + va_copy(args1, args); + len = vsnprintf(buf, len, message, args1); + va_end(args1); + + va_copy(args1, args); + buf = allocateMemory(comp, len + 1, sizeof(char)); + vsnprintf(buf, len + 1, message, args); + va_end(args1); + + // no need to distinguish between FMI versions since we're not using variadic arguments + comp->logger(comp->componentEnvironment, comp->instanceName, status, category, buf); + + freeMemory(comp, buf); +} + +void logEvent(ModelInstance *comp, const char *message, ...) { + + if (!comp->logEvents) return; + + va_list args; + va_start(args, message); + logMessage(comp, OK, "logEvents", message, args); + va_end(args); +} + +void logError(ModelInstance *comp, const char *message, ...) { + + if (!comp->logErrors) return; + + va_list args; + va_start(args, message); + logMessage(comp, Error, "logStatusError", message, args); + va_end(args); +} // default implementations #if NUMBER_OF_EVENT_INDICATORS < 1 @@ -70,31 +304,58 @@ void setContinuousStates(ModelInstance *comp, const double x[], size_t nx) {} void getDerivatives(ModelInstance *comp, double dx[], size_t nx) {} #endif - Status doStep(ModelInstance *comp, double t, double tNext) { - int stateEvent = 0; - int timeEvent = 0; + bool stateEvent, timeEvent; + +#if NUMBER_OF_EVENT_INDICATORS > 0 + double *temp = NULL; +#endif + +#if NUMBER_OF_STATES > 0 + double x[NUMBER_OF_STATES] = { 0 }; + double dx[NUMBER_OF_STATES] = { 0 }; +#endif - comp->time = t; + while (comp->time + FIXED_SOLVER_STEP < tNext + 0.1 * FIXED_SOLVER_STEP) { + +#if NUMBER_OF_STATES > 0 + getContinuousStates(comp, x, NUMBER_OF_STATES); + getDerivatives(comp, dx, NUMBER_OF_STATES); - while (comp->time < tNext) { + // forward Euler step + for (int i = 0; i < NUMBER_OF_STATES; i++) { + x[i] += FIXED_SOLVER_STEP * dx[i]; + } - if (comp->nextEventTimeDefined && comp->nextEventTime < tNext) { - solver_step(comp, comp->time, comp->nextEventTime, &comp->time, &stateEvent); - } else { - solver_step(comp, comp->time, tNext, &comp->time, &stateEvent); - } + setContinuousStates(comp, x, NUMBER_OF_STATES); +#endif + stateEvent = false; + +#if NUMBER_OF_EVENT_INDICATORS > 0 + getEventIndicators(comp, comp->z, NUMBER_OF_EVENT_INDICATORS); + + // check for zero-crossing + for (int i = 0; i < NUMBER_OF_EVENT_INDICATORS; i++) { + stateEvent |= (comp->prez[i] * comp->z[i]) <= 0; + } + + // remember the current event indicators + temp = comp->z; + comp->z = comp->prez; + comp->prez = temp; +#endif + // check for time event - if (comp->nextEventTimeDefined && (comp->time - comp->nextEventTime > -1e-5)) { - timeEvent = true; - } + timeEvent = comp->nextEventTimeDefined && comp->time >= comp->nextEventTime; + + // log events + if (timeEvent) logEvent(comp, "Time event detected at t=%g s.", comp->time); + if (stateEvent) logEvent(comp, "State event detected at t=%g s.", comp->time); if (stateEvent || timeEvent) { eventUpdate(comp); - timeEvent = 0; - stateEvent = 0; } // terminate simulation, if requested by the model in the previous step @@ -105,6 +366,7 @@ Status doStep(ModelInstance *comp, double t, double tNext) { return Discard; // enforce termination of the simulation loop } + comp->time += FIXED_SOLVER_STEP; } return OK; diff --git a/test_build.py b/test_build.py index b2830e1..b553c28 100644 --- a/test_build.py +++ b/test_build.py @@ -4,11 +4,13 @@ import shutil -fmus_dir = None # /path/to/fmi-cross-check/fmus -test_fmus_version = '0.0.1' +fmus_dir = os.path.join(os.path.dirname(__file__), 'fmus') # /path/to/fmi-cross-check/fmus +test_fmus_version = '0.0.2' try: from fmpy import simulate_fmu + from fmpy.util import compile_platform_binary + fmpy_available = True except: print("FMPy not available. Skipping simulation.") @@ -19,6 +21,7 @@ try: import fmpy + fmi3_available = 'fmi3' in dir(fmpy) except: pass @@ -32,13 +35,12 @@ models = ['BouncingBall', 'Dahlquist', 'Resource', 'Stair', 'VanDerPol', 'Feedthrough'] if os.name == 'nt': - generator = 'Visual Studio 14 2015 Win64' + generator = 'Visual Studio 15 2017 Win64' else: generator = 'Unix Makefiles' def copy_to_cross_check(build_dir, model_names, fmi_version, fmi_types): - if fmus_dir is None: return @@ -64,7 +66,7 @@ def setUpClass(cls): print("Removing " + build_dir) shutil.rmtree(build_dir) - def validate(self, build_dir, fmi_types=['ModelExchange', 'CoSimulation'], models=models): + def validate(self, build_dir, fmi_types=['ModelExchange', 'CoSimulation'], models=models, compile=False): from fmpy.util import read_csv, validate_result @@ -85,8 +87,12 @@ def validate(self, build_dir, fmi_types=['ModelExchange', 'CoSimulation'], model ref_csv = os.path.join(test_fmus_dir, model, model + '_ref.csv') for fmi_type in fmi_types: + ref = read_csv(ref_csv) + if compile: + compile_platform_binary(fmu_filename) + result = simulate_fmu(fmu_filename, fmi_type=fmi_type, start_values=start_values, @@ -146,6 +152,7 @@ def test_fmi2(self): if fmpy_available: self.validate(build_dir) + self.validate(build_dir, compile=True) copy_to_cross_check(build_dir=build_dir, model_names=models, fmi_version='2.0', fmi_types=['cs', 'me']) @@ -164,6 +171,7 @@ def test_fmi3(self): if fmi3_available: self.validate(build_dir, models=['BouncingBall', 'Dahlquist', 'Feedthrough', 'Resource', 'Stair', 'VanDerPol']) + copy_to_cross_check(build_dir=build_dir, model_names=models, fmi_version='3.0', fmi_types=['cs', 'me']) if __name__ == '__main__': unittest.main()