Skip to content

Commit

Permalink
FMU variable table mechanism for simplified FMU variable access.
Browse files Browse the repository at this point in the history
Signed-off-by: Timothy Rule (VM/EMT3) <[email protected]>
  • Loading branch information
timrulebosch committed Jan 24, 2025
1 parent 6a43fd5 commit fea1497
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 17 deletions.
22 changes: 12 additions & 10 deletions dse/examples/fmu/counter/counter.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,36 @@
//
// SPDX-License-Identifier: Apache-2.0

#include <dse/clib/util/strings.h>
#include <dse/clib/collections/hashmap.h>
#include <dse/fmu/fmu.h>

#define UNUSED(x) ((void)x)
#define VR_COUNTER "1"
typedef struct {
double counter;
} VarTable;

int fmu_create(FmuInstanceData* fmu)
{
UNUSED(fmu);
VarTable* v = malloc(sizeof(VarTable));
*v = (VarTable){
.counter = fmu_register_var(fmu, 1, false, offsetof(VarTable, counter)),
};
fmu_register_var_table(fmu, v);
return 0;
}

int fmu_init(FmuInstanceData* fmu)
{
hashmap_set_double(&(fmu->variables.scalar.output), VR_COUNTER, 0.0);
UNUSED(fmu);
return 0;
}

int fmu_step(
FmuInstanceData* fmu, double CommunicationPoint, double stepSize)
int fmu_step(FmuInstanceData* fmu, double CommunicationPoint, double stepSize)
{
UNUSED(CommunicationPoint);
UNUSED(stepSize);
VarTable* v = fmu_var_table(fmu);

/* Increment the counter. */
double* counter = hashmap_get(&fmu->variables.scalar.output, VR_COUNTER);
if (counter) *counter += 1;
v->counter += 1;
return 0;
}

Expand Down
23 changes: 22 additions & 1 deletion dse/fmu/fmi2fmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,14 @@ fmi2Component fmi2Instantiate(fmi2String instance_name, fmi2Type fmu_type,
/* Lazy free list. */
hashlist_init(&fmu->variables.binary.free_list, 1024);

/* Specialised Model. */
/* Create the FMU. */
if (fmu_create(fmu) != fmi2OK) {
fmu_log(fmu, fmi2Error, "Error", "The FMU was not created correctly!");
}
if (fmu->var_table.table) {
fmu_log(fmu, fmi2OK, "Debug", "FMU Var Table is not configured");
}

/* Return the created instance object. */
return (fmi2Component)fmu;
}
Expand Down Expand Up @@ -473,11 +477,21 @@ fmi2Status fmi2DoStep(fmi2Component c, fmi2Real currentCommunicationPoint,

/* Make sure that all binary signals were reset at some point. */
if (fmu->variables.vtable.reset) fmu->variables.vtable.reset(fmu);
/* Marshal Signal Vectors to the VarTable. */
for (FmuVarTableMarshalItem* mi = fmu->var_table.marshal_list;
mi && mi->variable; mi++) {
*mi->variable = *mi->signal;
}

/* Step the model. */
int32_t rc =
fmu_step(fmu, currentCommunicationPoint, communicationStepSize);

/* Marshal the VarTable to the Signal Vectors. */
for (FmuVarTableMarshalItem* mi = fmu->var_table.marshal_list;
mi && mi->variable; mi++) {
*mi->signal = *mi->variable;
}
/* Reset the binary signal reset mechanism. */
fmu->variables.signals_reset = false;

Expand Down Expand Up @@ -508,6 +522,13 @@ void fmi2FreeInstance(fmi2Component c)

if (fmu->variables.vtable.remove) fmu->variables.vtable.remove(fmu);

fmu_log(fmu, fmi2OK, "Debug", "Release var table");
free(fmu->var_table.table);
free(fmu->var_table.marshal_list);
if (fmu->var_table.var_list.hash_map.hash_function) {
hashlist_destroy(&fmu->var_table.var_list);
}

fmu_log(fmu, fmi2OK, "Debug", "Destroy the index");
hashmap_destroy(&fmu->variables.scalar.input);
hashmap_destroy(&fmu->variables.scalar.output);
Expand Down
23 changes: 22 additions & 1 deletion dse/fmu/fmi3fmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,15 @@ fmi3Instance fmi3InstantiateCoSimulation(fmi3String instanceName,
/* Lazy free list. */
hashlist_init(&fmu->variables.binary.free_list, 1024);

/* Specialised Model. */
/* Create the FMU. */
if (fmu_create(fmu) != fmi3OK) {
fmu_log(fmu, fmi3Error, "Error", "The FMU was not created correctly!");
}
if (fmu->var_table.table) {
fmu_log(fmu, fmi3OK, "Debug", "FMU Var Table is not configured");
}

/* Return the created instance object. */
return (fmi3Instance)fmu;
}

Expand Down Expand Up @@ -223,6 +227,13 @@ void fmi3FreeInstance(fmi3Instance instance)

if (fmu->variables.vtable.remove) fmu->variables.vtable.remove(fmu);

fmu_log(fmu, fmi3OK, "OK", "Release var table");
free(fmu->var_table.table);
free(fmu->var_table.marshal_list);
if (fmu->var_table.var_list.hash_map.hash_function) {
hashlist_destroy(&fmu->var_table.var_list);
}

fmu_log(fmu, fmi3OK, "Ok", "Destroy the index");
hashmap_destroy(&fmu->variables.scalar.input);
hashmap_destroy(&fmu->variables.scalar.output);
Expand Down Expand Up @@ -1142,11 +1153,21 @@ fmi3Status fmi3DoStep(fmi3Instance instance,

/* Make sure that all binary signals were reset at some point. */
if (fmu->variables.vtable.reset) fmu->variables.vtable.reset(fmu);
/* Marshal Signal Vectors to the VarTable. */
for (FmuVarTableMarshalItem* mi = fmu->var_table.marshal_list;
mi && mi->variable; mi++) {
*mi->variable = *mi->signal;
}

/* Step the model. */
int32_t rc =
fmu_step(fmu, currentCommunicationPoint, communicationStepSize);

/* Marshal the VarTable to the Signal Vectors. */
for (FmuVarTableMarshalItem* mi = fmu->var_table.marshal_list;
mi && mi->variable; mi++) {
*mi->signal = *mi->variable;
}
/* Reset the binary signal reset mechanism. */
fmu->variables.signals_reset = false;

Expand Down
40 changes: 35 additions & 5 deletions dse/fmu/fmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,39 @@
#define DLL_PRIVATE __attribute__((visibility("hidden")))
#endif

#define UNUSED(x) ((void)x)

/**
FMU API
=======
The FMU API provides a simplified FMU inteface with an abstracted varaible
interface (indexing and storage). The FMU Interface includes the methods:
* `[fmu_create()]({{< ref "#fmu_create" >}})`
* `[fmu_init()]({{< ref "#fmu_init" >}})`
* `[fmu_step()]({{< ref "#fmu_step" >}})`
* `[fmu_destroy()]({{< ref "#fmu_destroy" >}})`
* Implemented by FMU developer:
* `[fmu_create()]({{< ref "#fmu_create" >}})`
* `[fmu_init()]({{< ref "#fmu_init" >}})`
* `[fmu_step()]({{< ref "#fmu_step" >}})`
* `[fmu_destroy()]({{< ref "#fmu_destroy" >}})`
* Additional provided functions:
* `[fmu_log()]({{< ref "#fmu_log" >}})` - logging function
* Supporting Variable Table mechanism:
* `[fmu_register_var()]({{< ref "#fmu_register_var" >}})`
* `[fmu_register_var_table()]({{< ref "#fmu_register_var_table" >}})`
* `[fmu_var_table()]({{< ref "#fmu_var_table" >}})`
An additional FMU Signal Interface is available for more complex integrations:
* `[fmu_signals_reset()]({{< ref "#fmu_signals_reset" >}})`
* `[fmu_signals_setup()]({{< ref "#fmu_signals_setup" >}})`
* `[fmu_signals_remove()]({{< ref "#fmu_signals_remove" >}})`
FMUs imlemented using this simplified FMU API can be built for both FMI 2
and FMI 3 standards by linking to the relevant implementations:
* `fmi2fmu.c` for and FMI 2 FMU
* `fmi3fmu.c` for and FMI 3 FMU
Binary variables are supported for FMI 3 and FMI 2 standards.
In FMUs built to the FMI 2 standard, binary variables are implemented via
FMI String Variables and an associated encoding.
Expand Down Expand Up @@ -170,6 +181,12 @@ typedef struct FmuSignalVectorIndex {
} FmuSignalVectorIndex;


typedef struct FmuVarTableMarshalItem {
double* variable; // Pointer to FMU allocated storage.
double* signal; // Pointer to FmuSignalVector storage (i.e. scalar).
} FmuVarTableMarshalItem;


typedef struct FmuInstanceData {
/* FMI Instance Data. */
struct {
Expand Down Expand Up @@ -207,6 +224,15 @@ typedef struct FmuInstanceData {

/* FMU Instance Data (additional). */
void* data;

/* FMU Variable Table, used for indirect variable access. */
struct {
void* table;
HashList var_list;

/* NLT for var/signal mirroring. */
FmuVarTableMarshalItem* marshal_list;
} var_table;
} FmuInstanceData;


Expand All @@ -215,7 +241,11 @@ DLL_PRIVATE char* ascii85_encode(const char* source, size_t len);
DLL_PRIVATE char* ascii85_decode(const char* source, size_t* len);

/* signal.c (default implementations for generic FMU) */
DLL_PRIVATE void fmu_load_signal_handlers(FmuInstanceData* fmu);
DLL_PRIVATE void fmu_load_signal_handlers(FmuInstanceData* fmu);
DLL_PRIVATE double fmu_register_var(
FmuInstanceData* fmu, uint32_t vref, bool input, size_t offset);
DLL_PRIVATE void fmu_register_var_table(FmuInstanceData* fmu, void* table);
DLL_PRIVATE void* fmu_var_table(FmuInstanceData* fmu);

/* FMU Interface (example implementation in fmu.c) */
DLL_PRIVATE int32_t fmu_create(FmuInstanceData* fmu);
Expand Down
102 changes: 102 additions & 0 deletions dse/fmu/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ fmu (FmuInstanceData*)
*/
extern void fmu_signals_reset(FmuInstanceData* fmu);

/**
fmu_signals_setup
=================
Expand Down Expand Up @@ -306,3 +307,104 @@ void fmu_load_signal_handlers(FmuInstanceData* fmu)
dlsym(handle, FMU_SIGNALS_REMOVE_FUNC_NAME);
}
}


/**
fmu_register_var
================
Register a variable with the FMU Variable Table mechanism.
Parameters
----------
fmu (FmuInstanceData*)
: The FMU Descriptor object representing an instance of the FMU Model.
vref (uint32_t)
: Variable reference of the variable being registered.
input (bool)
: Set `true` for input, and `false` for output variable causality.
offset (size_t)
: Offse of the variable (type double) in the FMU provided variable table.
Returns
-------
start_value (double)
: The configured FMU Variable start value, or 0.
*/
double fmu_register_var(
FmuInstanceData* fmu, uint32_t vref, bool input, size_t offset)
{
double* signal = NULL;
char key[HASHLIST_KEY_LEN];

/* Lookup the signal. */
snprintf(key, HASHLIST_KEY_LEN, "%i", vref);
if (input) {
signal = hashmap_get(&fmu->variables.scalar.input, key);
} else {
signal = hashmap_get(&fmu->variables.scalar.output, key);
}
if (signal == NULL) return 0;

/* Create the marshal list. */
FmuVarTableMarshalItem* mi = malloc(sizeof(FmuVarTableMarshalItem));
*mi = (FmuVarTableMarshalItem){
.variable = (void*)offset, // Corrected in fmu_register_var_table.
.signal = signal,
};
if (fmu->var_table.var_list.hash_map.hash_function == NULL) {
hashlist_init(&fmu->var_table.var_list, 128);
}
hashlist_append(&fmu->var_table.var_list, mi);
return 0;
}


/**
fmu_register_var_table
======================
Register the Variable Table. The previouly registered variables, via calls to
`fmu_register_var`, are configured and the FMU Variable Table mechanism
is enabled.
Parameters
----------
fmu (FmuInstanceData*)
: The FMU Descriptor object representing an instance of the FMU Model.
table (void*)
: Pointer to the Variable Table being registered.
*/
void fmu_register_var_table(FmuInstanceData* fmu, void* table)
{
fmu->var_table.table = table;
fmu->var_table.marshal_list = hashlist_ntl(
&fmu->var_table.var_list, sizeof(FmuVarTableMarshalItem), true);
for (FmuVarTableMarshalItem* mi = fmu->var_table.marshal_list;
mi && mi->signal; mi++) {
/* Correct the varaible pointer offset, to vt base. */
mi->variable = (double*)(table + (size_t)mi->variable);
}
}


/**
fmu_var_table
=============
Return a reference to the previously registered Variable Table.
Parameters
----------
fmu (FmuInstanceData*)
: The FMU Descriptor object representing an instance of the FMU Model.
Returns
-------
table (void*)
: Pointer to the Variable Table.
*/
void* fmu_var_table(FmuInstanceData* fmu)
{
return fmu->var_table.table;
}
Loading

0 comments on commit fea1497

Please sign in to comment.