From 811a12f3d73b1a295e045448c4324c01e8800e0d Mon Sep 17 00:00:00 2001 From: "Timothy Rule (VM/EMT3)" Date: Thu, 14 Nov 2024 13:48:32 +0100 Subject: [PATCH] Add support for MDF measurement of FMI MCL. Signed-off-by: Timothy Rule (VM/EMT3) --- Makefile | 5 +- doc/content/apis/fmi/fmimcl/index.md | 43 +++-- doc/content/apis/fmi/fmimodelc/index.md | 156 +++++++++--------- doc/content/docs/user/fmi/fmimcl/index.md | 2 +- dse/fmimcl/CMakeLists.txt | 1 + dse/fmimcl/fmimcl.h | 15 +- dse/fmimcl/model.c | 50 +++++- dse/fmimodelc/fmimodelc.h | 8 + tests/testscript/e2e/fmimcl_measurement.txtar | 28 ++-- 9 files changed, 184 insertions(+), 124 deletions(-) diff --git a/Makefile b/Makefile index c4221f7..f01257c 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ ################ ## DSE Projects. -export DSE_CLIB_VERSION ?= 1.0.24 +export DSE_CLIB_VERSION ?= 1.0.26 export DSE_MODELC_VERSION ?= 2.1.9 @@ -137,9 +137,8 @@ ifeq ($(PACKAGE_ARCH), linux-amd64) @-docker kill simer @set -eu; for t in $(TESTSCRIPT_E2E_FILES) ;\ do \ - echo "Running E2E Test: $$t" ;\ export ENTRYWORKDIR=$$(mktemp -d) ;\ - echo "ENTRYWORKDIR: $${ENTRYWORKDIR}" ;\ + echo "Running E2E Test: $$t (in $${ENTRYWORKDIR})" ;\ docker run -i --rm \ -e ENTRYHOSTDIR=$(HOST_DOCKER_WORKSPACE) \ -e ENTRYWORKDIR=$${ENTRYWORKDIR} \ diff --git a/doc/content/apis/fmi/fmimcl/index.md b/doc/content/apis/fmi/fmimcl/index.md index 5dfbb28..c1055cd 100644 --- a/doc/content/apis/fmi/fmimcl/index.md +++ b/doc/content/apis/fmi/fmimcl/index.md @@ -196,27 +196,6 @@ center footer Dynamic Simulation Environment -## fmimcl_adapter_create - - -This method creates an adapter object based on the configuration in the FMU -Model object. - -### Parameters - -fmu_model (FmuModel*) -: FMU Model descriptor object. - -### Returns - -0 (int32_t) -: The related adapter was loaded by the fmimcl. - --EINVAL (-22) -: No matching adapter found. - - - ## mcl_create @@ -286,7 +265,7 @@ typedef struct FmuModel { void* m_doc; void* adapter; FmuData data; - struct (anonymous struct at dse/fmimcl/fmimcl.h:257:5) measurement; + struct (anonymous struct at dse/fmimcl/fmimcl.h:266:5) measurement; } ``` @@ -306,6 +285,26 @@ typedef struct FmuSignal { ## Functions +### fmimcl_adapter_create + +This method creates an adapter object based on the configuration in the FMU +Model object. + +#### Parameters + +fmu_model (FmuModel*) +: FMU Model descriptor object. + +#### Returns + +0 (int32_t) +: The related adapter was loaded by the fmimcl. + +-EINVAL (-22) +: No matching adapter found. + + + ### fmimcl_allocate_source For each Signal parsed from the Signalgroup, this function creates an diff --git a/doc/content/apis/fmi/fmimodelc/index.md b/doc/content/apis/fmi/fmimodelc/index.md index 9f502a0..e359a9f 100644 --- a/doc/content/apis/fmi/fmimodelc/index.md +++ b/doc/content/apis/fmi/fmimodelc/index.md @@ -2,6 +2,36 @@ title: FMI ModelC FMU API Reference linkTitle: ModelC FMU --- +## FMI ModelC FMU + + +The FMI ModelC FMU is and FMU which is capable of loading and running a +DSE Simulation (e.g. a ModelC Simulation Stack). All capabilites of the ModelC +Runtime are supported, including the exchange of binary signals (e.g. CAN) and +realisation of bus topologies (e.g. multi-node CAN Networks). + + +### Component Diagram + + + +![](fmimodelc-component.png) + + + + ## fmi2GetReal @@ -28,6 +58,37 @@ fmi2OK (fmi2Status) +## fmi2SetString + + +Set values for the provided list of value references and values. String/Binary +variables are always appended to the ModelC Binary Signal. + +> Note: If several variables are indexed against the same ModelC Binary Signal, + for instance in a Bus Topology, then each variable will be appended to that + ModelC Binary Signal. + +### Parameters + +c (fmi2Component*) +: An Fmu2InstanceData object representing an instance of this FMU. + +vr (fmi2ValueReference[]) +: List of value references to set. + +nvr (int) +: The number of value references to set. + +value (fmi2String[]) +: Storage for the values to be set. + +### Returns + +fmi2OK (fmi2Status) +: The requested variables have been set (where available). + + + ## fmi2DoStep @@ -62,6 +123,23 @@ fmi2Error (fmi2Status) +## fmi2Instantiate + + +Create an instance of this FMU, allocate/initialise a Fmu2InstanceData +object which should be used for subsequent calls to FMI methods (as parameter +`fmi2Component c`). + +> Note: This implementation __does not__ use memory related callbacks provided + by the Importer (e.g. `malloc()` or `free()`). + +### Returns + +fmi2Component (pointer) +: An Fmu2InstanceData object which represents this FMU instance. + + + ## fmi2ExitInitializationMode @@ -135,37 +213,6 @@ fmi2OK (fmi2Status) -## fmi2SetString - - -Set values for the provided list of value references and values. String/Binary -variables are always appended to the ModelC Binary Signal. - -> Note: If several variables are indexed against the same ModelC Binary Signal, - for instance in a Bus Topology, then each variable will be appended to that - ModelC Binary Signal. - -### Parameters - -c (fmi2Component*) -: An Fmu2InstanceData object representing an instance of this FMU. - -vr (fmi2ValueReference[]) -: List of value references to set. - -nvr (int) -: The number of value references to set. - -value (fmi2String[]) -: Storage for the values to be set. - -### Returns - -fmi2OK (fmi2Status) -: The requested variables have been set (where available). - - - ## fmi2FreeInstance @@ -178,53 +225,6 @@ c (fmi2Component*) -## FMI ModelC FMU - - -The FMI ModelC FMU is and FMU which is capable of loading and running a -DSE Simulation (e.g. a ModelC Simulation Stack). All capabilites of the ModelC -Runtime are supported, including the exchange of binary signals (e.g. CAN) and -realisation of bus topologies (e.g. multi-node CAN Networks). - - -### Component Diagram - - - -![](fmimodelc-component.png) - - - - -## fmi2Instantiate - - -Create an instance of this FMU, allocate/initialise a Fmu2InstanceData -object which should be used for subsequent calls to FMI methods (as parameter -`fmi2Component c`). - -> Note: This implementation __does not__ use memory related callbacks provided - by the Importer (e.g. `malloc()` or `free()`). - -### Returns - -fmi2Component (pointer) -: An Fmu2InstanceData object which represents this FMU instance. - - - ## Typedefs ## Functions diff --git a/doc/content/docs/user/fmi/fmimcl/index.md b/doc/content/docs/user/fmi/fmimcl/index.md index a187526..db62d84 100644 --- a/doc/content/docs/user/fmi/fmimcl/index.md +++ b/doc/content/docs/user/fmi/fmimcl/index.md @@ -1,6 +1,6 @@ --- title: "FMI Model Compatibility Library (MCL)" -linkTitle: "FMI MCL" +linkTitle: "MCL" weight: 400 tags: - FMI diff --git a/dse/fmimcl/CMakeLists.txt b/dse/fmimcl/CMakeLists.txt index 33814b9..c558844 100644 --- a/dse/fmimcl/CMakeLists.txt +++ b/dse/fmimcl/CMakeLists.txt @@ -29,6 +29,7 @@ add_library(fmimcl SHARED parser.c adapter/fmi2mcl.c ../fmimodelc/ascii85.c + ${DSE_CLIB_SOURCE_DIR}/clib/mdf/mdf.c ) target_include_directories(fmimcl PRIVATE diff --git a/dse/fmimcl/fmimcl.h b/dse/fmimcl/fmimcl.h index bbcf8d0..0b4e2c5 100644 --- a/dse/fmimcl/fmimcl.h +++ b/dse/fmimcl/fmimcl.h @@ -8,10 +8,19 @@ #include #include #include +#include #include #include +#ifndef DLL_PUBLIC +#define DLL_PUBLIC __attribute__((visibility("default"))) +#endif +#ifndef DLL_PRIVATE +#define DLL_PRIVATE __attribute__((visibility("hidden"))) +#endif + + /** FMI Model Compatibility Library =============================== @@ -255,9 +264,9 @@ typedef struct FmuModel { FmuData data; /* Measurement file. */ struct { - char* file_name; - void* file; - // TODO measurement descriptor + char* file_name; + void* file; + MdfDesc mdf; } measurement; } FmuModel; diff --git a/dse/fmimcl/model.c b/dse/fmimcl/model.c index ab401f9..f89b5cc 100644 --- a/dse/fmimcl/model.c +++ b/dse/fmimcl/model.c @@ -7,9 +7,10 @@ #include #include #include +#include #include -#include #include +#include #define UNUSED(x) ((void)x) @@ -29,6 +30,34 @@ char* _get_measurement_file_name(ModelDesc* model) } +static MdfChannelGroup* __create_cg_list(SignalVector* sv_save, size_t* count) +{ + if (sv_save == NULL || count == NULL) return NULL; + HashList g_list; + hashlist_init(&g_list, 10); + + /* Build the Channel Group list. */ + for (SignalVector* sv = sv_save; sv && sv->name; sv++) { + if (sv->is_binary) continue; + + MdfChannelGroup* cg = calloc(1, sizeof(MdfChannelGroup)); + *cg = (MdfChannelGroup){ + .name = sv->name, + .signal = sv->signal, + .scalar = sv->scalar, + .count = sv->count, + }; + hashlist_append(&g_list, cg); + } + + /* Construct the return object. */ + *count = hashlist_length(&g_list); + MdfChannelGroup* list = hashlist_ntl(&g_list, sizeof(MdfChannelGroup), true); + hashlist_destroy(&g_list); + return list; +} + + ModelDesc* model_create(ModelDesc* model) { int32_t rc; @@ -47,11 +76,16 @@ ModelDesc* model_create(ModelDesc* model) if (fmu->measurement.file_name) { errno = 0; fmu->measurement.file = fopen(fmu->measurement.file_name, "wb"); - if (fmu->measurement.file == NULL) + if (fmu->measurement.file == NULL) { log_fatal("Failed to open measurement file: %s", fmu->measurement.file_name); - // TODO configure the measurement interface - // (fmu_model->data.count/name/scalar) + } + /* Configure the measurement interface. */ + size_t group_count = 0; + MdfChannelGroup* group_list = __create_cg_list(model->sv, &group_count); + fmu->measurement.mdf = mdf_create(fmu->measurement.file, group_list, group_count); + mdf_start_blocks(&fmu->measurement.mdf); + free(group_list); } /* Return the extended object (FmuModel). */ @@ -81,9 +115,14 @@ int model_step(ModelDesc* model, double* model_time, double stop_time) { int32_t rc; MclDesc* m = (MclDesc*)model; + FmuModel* fmu = (FmuModel*)m; - // TODO call measurement interface + /* Call the measurement interface. */ + if (fmu->measurement.file) { + mdf_write_records(&fmu->measurement.mdf, *model_time); + } + /* Step the FMU. */ __trace_sv(model->sv); rc = mcl_marshal_out(m); if (rc != 0) return rc; @@ -110,7 +149,6 @@ void model_destroy(ModelDesc* model) /* Finalise measurement. */ FmuModel* fmu = (FmuModel*)m; if (fmu->measurement.file) { - // TODO finalise the measurement interface fclose(fmu->measurement.file); fmu->measurement.file = NULL; } diff --git a/dse/fmimodelc/fmimodelc.h b/dse/fmimodelc/fmimodelc.h index 82ee083..9eb1c36 100644 --- a/dse/fmimodelc/fmimodelc.h +++ b/dse/fmimodelc/fmimodelc.h @@ -11,6 +11,14 @@ #include +#ifndef DLL_PUBLIC +#define DLL_PUBLIC __attribute__((visibility("default"))) +#endif +#ifndef DLL_PRIVATE +#define DLL_PRIVATE __attribute__((visibility("hidden"))) +#endif + + /** FMI ModelC FMU ============== diff --git a/tests/testscript/e2e/fmimcl_measurement.txtar b/tests/testscript/e2e/fmimcl_measurement.txtar index 3e2deeb..4c526e2 100644 --- a/tests/testscript/e2e/fmimcl_measurement.txtar +++ b/tests/testscript/e2e/fmimcl_measurement.txtar @@ -1,13 +1,21 @@ +# Operate from the WORKDIR (allocated by caller, i.e. docker command). +cd $WORKDIR +env SIM=sim env NAME=fmu_inst -env SIM=dse/build/_out/fmimcl -env MEASUREMENT_FILE=examples/data/measurement.mf4 +env MDF_FILE=measurement.mf4 -# TEST: FMI MCL with Measurement -rm /repo/$SIM/data/measurement.mf4 -exec sh -e $WORK/test.sh -exists /repo/$SIM/$MEASUREMENT_FILE +# Create the simulation folder (Simer layout) +mkdir $SIM +exec cp -r $REPODIR/dse/build/_out/fmimcl/. sim + +# TEST: FMI MCL with Measurement +rm $SIM/$MDF_FILE +! exists $SIM/$MDF_FILE +exec ls -l -R /repo/$SIM/ +exec sh -e $WORK/test.sh +exists $SIM/$MDF_FILE stdout 'Load YAML File: examples/data/simulation.yaml' stdout 'Loading symbol: model_create ... ok' @@ -15,17 +23,15 @@ stdout 'Loading symbol: model_step ... ok' stdout 'Loading symbol: model_destroy ... ok' stdout 'Run the Simulation ...' stdout 'Controller exit ...' -stdout 'Measurement File: /sim/examples/data/measurement.mf4' - +stdout 'Measurement File: /sim/measurement.mf4' -# TODO check measurement file content -- test.sh -- SIMER="${SIMER:-ghcr.io/boschglobal/dse-simer:latest}" -docker run --name simer -i --rm -v $ENTRYHOSTDIR/$SIM:/sim \ +docker run --name simer -i --rm -v $ENTRYWORKDIR/$SIM:/sim \ $SIMER -valgrind $NAME -logger 2 \ -env simbus:SIMBUS_LOGLEVEL=5 \ -env extended_inst:SIMBUS_LOGLEVEL=5 \ -env $NAME:SIMBUS_LOGLEVEL=4 \ - -env $NAME:MEASUREMENT_FILE=/sim/$MEASUREMENT_FILE \ + -env $NAME:MEASUREMENT_FILE=/sim/$MDF_FILE \ -stepsize 0.0005 -endtime 0.005