Model API Reference
Model API
The Model API allows model developers and integrators to implement models which can be connected to a Simulation Bus. Models are able to exchange signals with other models via this connection to a Simulation Bus. A runtime environment, such as the ModelC Runtime/Importer, will load the model and also manages the connection with the Simulation Bus.
The Model API provides two simple interfaces which facilitate the development of models; the Model Interface which is concerned with the model lifecycle; and the Signal Interface which facilitates signal exchange.
Model Interface
The Model Interface provides the necessary types, methods and objects required for implementing a model. Such a model can easily participate in a simulation by being connecting to a Simulation Bus (using the ModelC Importer) and then exchanging signals with other models in that simulation by using the provided SignalVector objects (which represent those signals).
Additionally, model implementers may extend or modify the Model Interface to support more complex integrations.
Signal Vector Interface
Models exchange signals via the Simulation Bus using a Signal Vector. Signal
Vectors represent a logical grouping of signals (e.g. a collection of signals
belonging to an ECU interface or bus). They are defined by a SignalGroup
schema kind and may be configured to represent either either scalar
(double, int, bool) or binary values.
Component Diagram
@startuml model-interface
skinparam nodesep 55
skinparam ranksep 40
title Model Interface
component "Model" as m1
component "Model" as m2
interface "SimBus" as SBif
m1 -left-> SBif
m2 -right-> SBif
package "Model" {
component "Runtime" as ModelC
interface "ModelVTable" as Mvt
component "Model" as Mdl
}
SBif <-down- ModelC
Mdl -up- Mvt
Mvt )-up- ModelC
center footer Dynamic Simulation Environment
@enduml
Example (Model Interface)
#include <errno.h>
#include <stddef.h>
#include <dse/modelc/model.h>
#define UNUSED(x) ((void)x)
ModelDesc* model_create(ModelDesc* m)
{
return (ModelDesc*)m;
}
int model_step(ModelDesc* m, double* model_time, double stop_time)
{
ModelSignalIndex counter = m->index(m, "data", "counter");
if (counter.scalar == NULL) return -EINVAL;
*(counter.scalar) += 1;
*model_time = stop_time;
return 0;
}
void model_destroy(ModelDesc* m)
{
UNUSED(m);
}
Example (Signal Vector Interface)
#include <string.h>
#include <dse/modelc/model.h>
#include <dse/logger.h>
#define UNUSED(x) ((void)x)
int model_step(ModelDesc* m, double* model_time, double stop_time)
{
SignalVector* sv = m->sv;
while (sv && sv->name) {
log_debug("Signal Vector : %s", sv->name);
for (uint32_t i = 0; i < sv->count; i++) {
log_debug(" signal : %s", sv->signal[i]);
if (sv->is_binary) {
log_debug(" length : %s", sv->length[i]);
log_debug(" buffer_size : %s", sv->buffer_size[i]);
log_debug(" mime_type : %s", sv->mime_type[i]);
// Example use of object functions.
void* data = strdup("foo");
signal_reset(sv, i);
signal_append(sv, i, data, strlen("foo"));
free(data);
signal_release(sv, i);
const char* mime_type =
signal_annotation(sv, i, "mime_type", NULL);
if (mime_type) log_debug(" annotation : %s", mime_type);
} else {
log_debug(" scalar : %s", sv->scalar[i]);
}
}
// Next signal vector.
sv++;
}
*model_time = stop_time;
return 0;
}
Typedefs
ModelDesc
typedef struct ModelDesc {
ModelIndex index;
SimulationSpec* sim;
ModelInstanceSpec* mi;
SignalVector* sv;
ModelVTable vtable;
uint64_t [4] __reserved__;
}
ModelSignalIndex
typedef struct ModelSignalIndex {
SignalVector* sv;
double* scalar;
void** binary;
uint32_t vector;
uint32_t signal;
}
ModelVTable
typedef struct ModelVTable {
ModelCreate create;
ModelStep step;
ModelDestroy destroy;
ModelIndex index;
void *[2] __reserved__;
}
SignalVector
typedef struct SignalVector {
const char* name;
const char* alias;
const char* function_name;
ModelInstanceSpec* mi;
void* index;
void* index_uid;
uint32_t count;
const char** signal;
_Bool is_binary;
SignalVectorVTable vtable;
uint64_t [8] __reserved__;
}
SignalVectorVTable
typedef struct SignalVectorVTable {
BinarySignalAppendFunc append;
BinarySignalResetFunc reset;
BinarySignalReleaseFunc release;
SignalAnnotationGetFunc annotation;
BinarySignalCodecFunc codec;
SignalGroupAnnotationGetFunc group_annotation;
void *[2] __reserved__;
}
Functions
model_annotation
Retrieve the specified annotation from the Model specification.
Parameters
- model (ModelDesc*)
- The Model Descriptor object representing an instance of this model.
- name (const char*)
- The name of the annotation.
Returns
- const char*
- The value of the specified annotation.
- NULL
- The specified annotation was not found.
model_create
Optional method of
ModelVTable
interface.
Called by the Model Runtime to create a new instance of this model.
The model_create()
method may extend or mutilate the provided Model
Descriptor. When extending the Model Descriptor and allocating additional
resources then the model_destroy()
method should also be implemented.
Fault conditions can be communicated to the caller by setting variable
errno
to a non-zero value. Additionally, log_fatal()
can be used to
immediately halt execution of a model.
Parameters
- model (ModelDesc*)
- The Model Descriptor object representing an instance of this model.
Returns
- NULL
- The Channel was configured.
- (ModelDesc*)
- Pointer to a new, or mutilated, version of the Model Descriptor object. The
original Model Descriptor object will be released by the Model Runtime (i.e.
don’t call
free()
). - errno <> 0 (indirect)
- Indicates an error condition.
Example
#include <stddef.h>
#include <string.h>
#include <dse/modelc/model.h>
#include <dse/logger.h>
typedef struct {
ModelDesc model;
/* Signal Pointers. */
struct {
double* counter;
} signals;
} ExtendedModelDesc;
static inline double* _index(ExtendedModelDesc* m, const char* v, const char* s)
{
ModelSignalIndex idx = signal_index((ModelDesc*)m, v, s);
if (idx.scalar == NULL) log_fatal("Signal not found (%s:%s)", v, s);
return idx.scalar;
}
ModelDesc* model_create(ModelDesc* model)
{
/* Extend the ModelDesc object (using a shallow copy). */
ExtendedModelDesc* m = calloc(1, sizeof(ExtendedModelDesc));
memcpy(m, model, sizeof(ModelDesc));
/* Index the signals that are used by this model. */
m->signals.counter = _index(m, "data", "counter");
/* Set initial values. */
*(m->signals.counter) = 42;
/* Return the extended object. */
return (ModelDesc*)m;
}
int model_step(ModelDesc* model, double* model_time, double stop_time)
{
ExtendedModelDesc* m = (ExtendedModelDesc*)model;
*(m->signals.counter) += 1;
*model_time = stop_time;
return 0;
}
model_destroy
Optional method of
ModelVTable
interface.
Called by the Model Runtime at the end of a simulation, the model_destroy()
function may be implemented by a Model Integrator to perform any custom
cleanup operations (e.g. releasing instance related resources, such as open
files or allocated memory).
Parameters
- model (ModelDesc*)
- The Model Descriptor object representing an instance of this model.
model_expand_vars
Expand environment variables in a string according to typical shell variable expansion (i.e ${FOO} or ${BAR:-default}). Environment variables are searched first with the Model Instance Name prefixed to the variable name with a “__” delimiter (i.e. “INSTANCE_NAME__ENVAR”), and then if no match is found, the search is repated with only the provied variable name.
Note: Variables are converted to upper case before searching the environment.
Parameters
- model (ModelDesc*)
- The Model Descriptor object representing an instance of this model.
- source (const char*)
- The string containing environment variables to expand.
Returns
- char*
- String with environment variables expanded. Caller to free.
model_index_
Provided method (by the Runtime). Model implementers may specify a different index method by mutilating the Model Descriptor in the
model_create()
method, or even at runtime.
A model may use this method to index a signal that is contained within the Signal Vectors of the Model Descriptor.
Parameters
- model (ModelDesc*)
- The Model Descriptor object representing an instance of this model.
- vname (const char*)
- The name (alias) of the Signal Vector.
- sname (const char*)
- The name of the signal within the Signal Vector. When set to NULL the index will match on Signal Vector (vanme) only.
Returns
- ModelSignalIndex
- An index. When valid, either the
scalar
orbinary
fields will be set to a valid pointer (i.e. not NULL). Whensname
is not specified the index will contain a valid pointer to a Signal Vector object only (i.e. bothscalar
andbinary
will be set to NULL).
model_instance_annotation
Retrieve the specified annotation from the Model instance (Stack specification).
Parameters
- model (ModelDesc*)
- The Model Descriptor object representing an instance of this model.
- name (const char*)
- The name of the annotation.
Returns
- const char*
- The value of the specified annotation.
- NULL
- The specified annotation was not found.
model_step
Mandatory method of
ModelVTable
interface. Alternatively, Model implementers may specify theModelVTable.step
method dynamically by mutilating the Model Descriptor in themodel_create()
method, or even at runtime.
Called by the Model Runtime to step the model for a time interval.
Parameters
- model (ModelDesc*)
- The Model Descriptor object representing an instance of this model.
- model_time (double*)
- (in/out) Specifies the model time for this step of the model.
- stop_time (double)
- Specifies the stop time for this step of the model. The model step should not exceed this time.
Returns
- 0
- The step completed without error.
- <>0
- An error occurred at some point during the step execution.
- model_time (via parameter)
- The final model time reached for this step. This value may be less than
stop_time
if a step decides to return early.
signal_annotation
Get an annotation from a signal definition.
Parameters
- sv (SignalVector*)
- The Signal Vector object containing the signal.
- index (uint32_t)
- Index of the signal in the Signal Vector object.
- name (const char*)
- The name of the annotation.
Returns
- const char*
- The annotation value.
- NULL
- The requested annotation was not found, inspect
errno
for additional information..
Example (Annotation Specification)
kind: SignalGroup
metadata:
name: data
spec:
signals:
- signal: counter
annotations:
initial_value: 10
Example (Code Usage)
#include <stdlib.h>
#include <dse/modelc/model.h>
ModelDesc* model_create(ModelDesc* m)
{
ModelSignalIndex idx = m->index(m, "data", "counter");
if (idx.scalar) {
/* Set initial value. */
const char* v =
signal_annotation(idx.sv, idx.signal, "initial_value", NULL);
if (v) *(idx.scalar) = atoi(v);
}
return m;
}
signal_append
Append data to the end of the specified binary signal. The append method will resize the buffers of the binary signal as required.
Parameters
- sv (SignalVector*)
- The Signal Vector object containing the signal.
- index (uint32_t)
- Index of the signal in the Signal Vector object.
- data (uint8_t*)
- Address/pointer to the data which should be appended to the binary signal.
- len (size_t)
- Length of the provided data buffer being appended.
Returns
- 0
- The operation completed without error.
- -EINVAL (-22)
- Bad arguments.
- -ENOSYS (-88)
- The called function is not available.
- <>0
- Indicates an error condition. Inspect
errno
for additional information.
signal_codec
Return a pointer to the Codec object associated with a binary signal.
Codec objects are created when a binary signal is specified with a mime_type
annotation.
Parameters
- sv (SignalVector*)
- The Signal Vector object containing the signal.
- index (uint32_t)
- Index of the signal in the Signal Vector object.
Returns
- void*
- The Codec object associated with the binary signal.
- NULL
- The binary signal does not have an associated Codec object, inspect
errno
for additional information..
Example (Codec Specification)
kind: SignalGroup
metadata:
name: network
labels:
channel: network_vector
annotations:
vector_type: binary
spec:
signals:
- signal: can_bus
annotations:
mime_type: application/x-automotive-bus; interface=stream; type=frame;
bus=can; schema=fbs; bus_id=1; node_id=2; interface_id=3
Reference
signal_group_annotation
Get an annotation from a signal group.
Parameters
- sv (SignalVector*)
- The Signal Vector object representing the signal group.
- name (const char*)
- The name of the annotation.
Returns
- const char*
- The annotation value.
- NULL
- The requested annotation was not found, inspect
errno
for additional information..
signal_index
A model may use this method to index a signal that is contained within the Signal Vectors of the Model Descriptor.
Parameters
- model (ModelDesc*)
- The Model Descriptor object representing an instance of this model.
- vname (const char*)
- The name (alias) of the Signal Vector.
- name (const char*)
- The name of the signal within the Signal Vector. When set to NULL the index will match on Signal Vector (vanme) only.
Returns
- ModelSignalIndex
- An index. When valid, either the
scalar
orbinary
fields will be set to a valid pointer (i.e. not NULL). Whensname
is not specified the index will contain a valid pointer to a Signal Vector object only (i.e. bothscalar
andbinary
will be set to NULL).
signal_read
Read data from the specified binary signal. Returns pointer to the internal binary signal buffers.
Parameters
- sv (SignalVector*)
- The Signal Vector object containing the signal.
- index (uint32_t)
- Index of the signal in the Signal Vector object.
- data (uint8_t*)
- Address/pointer to the data of the binary signal.
- len (size_t)
- Length of the data.
Returns
- 0
- The operation completed without error.
- -EINVAL (-22)
- Bad arguments.
- <>0
- Indicates an error condition. Inspect
errno
for additional information.
signal_release
Release the resources allocated to a binary signal (e.g. free the buffer).
Parameters
- sv (SignalVector*)
- The Signal Vector object containing the signal.
- index (uint32_t)
- Index of the signal in the Signal Vector object.
Returns
- 0
- The operation completed without error.
- -EINVAL (-22)
- Bad arguments.
- -ENOSYS (-88)
- The called function is not available.
- <>0
- Indicates an error condition. Inspect
errno
for additional information.
signal_reset
Reset a binary signal (e.g. sets its buffer length to 0). The buffers of the
binary signal are not released (see signal_release()
).
Parameters
- sv (SignalVector*)
- The Signal Vector object containing the signal.
- index (uint32_t)
- Index of the signal in the Signal Vector object.
Returns
- 0
- The operation completed without error.
- -EINVAL (-22)
- Bad arguments.
- -ENOSYS (-88)
- The called function is not available.
- <>0
- Indicates an error condition. Inspect
errno
for additional information.
signal_reset_called
Indicate if reset has been called, and thusly its contained data consumed (by the caller).
Parameters
- sv (SignalVector*)
- The Signal Vector object containing the signal.
- index (uint32_t)
- Index of the signal in the Signal Vector object.
- reset_called (bool*)
- (out) The reset_called value of the specified binary signal.
Returns
- 0
- The operation completed without error.
- -EINVAL (-22)
- Bad arguments.
- <>0
- Indicates an error condition. Inspect
errno
for additional information.