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

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 or binary fields will be set to a valid pointer (i.e. not NULL). When sname is not specified the index will contain a valid pointer to a Signal Vector object only (i.e. both scalar and binary 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 the ModelVTable.step method dynamically by mutilating the Model Descriptor in the model_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

Network Codec API

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 or binary fields will be set to a valid pointer (i.e. not NULL). When sname is not specified the index will contain a valid pointer to a Signal Vector object only (i.e. both scalar and binary 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.