ifex

IFEX CORE SPECIFICATION

(C) 2022, 2023 - MBition GmbH
(C) 2022 - COVESA
(C) 2021 - Magnus Feuer

This document contains an introduction to the Interface Exchange (IFEX) framework and specification of the core Interface Description Language/Model (also known as ifex-idl and ifex-core). IFEX is the name for the technology (language, tools, etc.) behind the Vehicle Service Catalog (VSC) project.

License: Creative Commons Attribution 4.0 International License (CC-BY-4.0), described here

Documentation generated from: 8b82cf9e114b9d5c94b4ea783da640b2c062039e


FEATURES

The format supports the following features

Features that are not included in the core IDL, but worth describing:

This current version of the IFEX Core IDL has removed the “service:” keyword but the concept of a service should still be defined in terms of the interfaces the service provides. (See next chapter).

IFEX Core IDL does not include the name Signal but supports both Events and Properties that do cover two different interpretations of the word signal. See next chapter for a more detailed analysis.


Feature concept details

Target Environment

For example:

Please note that in the text, “target-environment dependent”, may sometimes be shortened to simply target-dependent and similar simplifications may occur.

Namespace

Interface

Method

Errors

Event

Avoiding event confusion:

Property

Return values

Return/status communication in asynchronous vs. synchronous method calls

Data Streams

It might be noticed that the IFEX core IDL does not seem to have an explicit type for “streaming data” type interfaces. However, when reading the previous chapter about asynchronous methods it should be clear that there is a natural way to do it:

  1. Define a method on the server, which the client would call to initiate a stream.
  2. Define the method return value as the datatype that describes the items that are streamed. (each byte, each packet, or to whatever level of detail the stream can be subdivided).
  3. When executing, the server streams continuous “updates” of this return value type for as long as the stream is open.

Note here, that the explicit separation of Error s from return value, which is most other programming and IDL environments fail to do, enables modeling Error information that would be returned to the client when a problem occurs, and that is completely independent of the return type that describes what the streaming data looks like.

Here we have a situation where it is likely to be known at interface-definition time how the method is intended to be used. It is therefore recommended that the interface designer makes note of this in a comment, that the particular method indicates a streaming interface.

As described in the previous chapter, to make a method asynchronous will require a target-environment deployment layer that annotates the method to be “asynchronous”. If it is necessary, the design of that target mapping is of course free to use more explicit metadata to make it even clearer and since Layer Types are completely open for design, it could also add more information such as the data rate and exact method of streaming, and so on.

Example (not normative):

methods:
  - name: mymethod
    asynchronous: true
    streaming: true    # example, the layer design (i.e. not part of this IDL core specification) decides what makes sense

Service (not a core IDL feature)

The current version of the IFEX Core IDL removed the “service:” keyword that was in the earliest specification. In the future a new concept may be appropriate to add but at the moment it is expected that any definition of “service” will be defined externally from the core IDL, and make reference to IFEX Core IDL file(s) that describe Interfaces.

It is still worthwhile to prepare our understanding of what a service is:

Signals (not a core IDL feature)

The word Signal is interpreted by some as the transfer of a value associated with a name/id for what that value represents. This suggests that a signal is something happening at a point in time. Such value transfer ought to be semantically equivalent to single-argument Event, and is therefore supported using Event in IFEX. Another interpretation is that the word Signal rather represents the underlying data item itself, independent of what time update-events are sent. In this second case, value-transfers are defined more as a consequence of the data changing, or a predefined frequency of update - for example “subscribing to changes of a Signal”. In this second interpretation the Signal is represented by a Property in IFEX. The Vehicle Signal Specification (VSS) defines “signals” that have a name, a datatype, a unit and meaning but the transfer of data is defined by separate protocols built on top of VSS. In other words, VSS typically uses the the second interpretation, and VSS Signals can then be represented by Properties in IFEX. (Refer to the separate analysis of the IFEX/VSS relationship).


NAMESPACE VERSIONING

IFEX namespaces can have a major and minor version, specified by major_version and minor_version keys, complemented by an additional free format string named version_label. These are defined as optional in the core IDL, but most development processes ought to treat them as mandatory at some point in the working process, to support management of API compatibility between components.

There is also an optional patch_version number, to simplify compatibility with other interface descriptions that use the semantic versioning principle. (Please see the recommendation against using patch_version in new designs, described in the Interface Versioning chapter).

Namespace version management can be used to understand the impact of changes on various namespace levels and how this may affect compatibility. For general API-compatibility evaluation, the version on the Interface node might be used more often, however.

Namespace versioning shall follow the Semantic Versioning principle. Follow the more detailed description under Interface Versioning.

Namespace versioning can be used build-time to ensure that the correct version of all needed namespace implementations are deployed.

INTERFACE VERSIONING

An Interface is essentially a specialization of the Namespace concept. Interfaces shall be versioned in the same manner, and are even more important since they are the likely place to determine if components will communicate correctly (i.e. use the compatible versions of Interface description).

There could be rules implemented in validation tools that ensure interface versions match the versioning of namespaces. (E.g. Don’t claim an interface is compatible if its parent namespace has changed in an incompatible way).

Versioning shall follow the Semantic Versioning principle:

Bumped minor numbers identifies backward-compatible additions to the previous version. This means that if a client requires version 1.3 of a server namespace and its methods, it knows that it can safely interface a server implementation of version 1.4 of that same namespace.

Bumped major versions identifies non-compatible changes to the previous version, such as a changed method signature. This means that a client requiring version 1.3 of a server knows that it cannot invoke a version 2.0 server implementation of that interface since that implementation is not backward compatible.

For details that are not mentioned here, refer to https://semver.org, but if there is a contradiction, this specification shall take precedence.

An optional field for patch version exists but it is recommended to avoid it in most development processes since minor (or major) version should be used indicate an actual change to the interface. Unlike code, in this context we are mostly interested in if there is a change in the interface and if it is backwards compatible.

If patch_version is used anyway, it ought to be for documentation related changes but small changes such as formatting or fixing a spelling mistake in comments might also be recognized by using the git commit hash, which is a guaranteed unique identifier of the IDL file contents. A processes that uses that commit hash won’t rely on a human remembering to update it, and could be an efficient replacement compared to constantly having to update the patch-level version number. In the end, it is however up to each adopter to decide on which process they want to use.

Note also the additional field version_label that is a free-form field in which implementations can place their own type of identifying information. Companies might have their own compatibility information, their own way of describing interface versions, or other information that goes into this free-form text. Some kind of unique hash of the interface content could be useful, but it then requires hashing the content without the version_label line itself of course.

If the file is going to be transferred from one git to another system, the process might fill in the version_label using the git commit hash after the file is taken out of git and transferred to that other system. But it is of course not possible write the commit hash into the file beforehand and commit that content into git (because the actual commit hash is not decided until the file is committed - and it would effectively depend on itself). In these cases, perhaps the development process may choose to perform another hash calculation (SHA/MD5) of the interface content (again leaving out of course the version_label lines themselves since there would otherwise be a circular dependency again).

Ultimately, versioning can be used build-time to ensure that the correct version of all needed interface/namespace implementations are deployed, while also detecting if multiple, non-compatible versions of an interface are required to be supported at the same time.


FUNDAMENTAL TYPES

These are the supported fundamental (primitive) types, as generated from the source code model. These primitive types are identical to the types used in the VSS (Vehicle Signal Specification) model, and of course they should easily match typical datatypes in other interface description systems.

Name Description Min value Max value
uint8 unsigned 8-bit integer 0 255
int8 signed 8-bit integer -128 127
uint16 unsigned 16-bit integer 0 65535
int16 signed 16-bit integer -32768 32767
uint32 unsigned 32-bit integer 0 4294967295
int32 signed 32-bit integer -2147483648 2147483647
uint64 unsigned 64-bit integer 0 2^64 - 1
int64 signed 64-bit integer -2^63 2^63 - 1
boolean boolean value False True
float floating point number -3.4e -38 3.4e 38
double double precision floating point number -1.7e -300 1.7e 300
string character string N/A N/A

LAYERS CONCEPT

The IFEX approach implements a layered approach to the definition of interfaces, and potentially other aspects of a system. The core interface file (Interface Description Language, or Interface Description Model) shall contain only a generic interface description that can be as widely applicable as possible.

As such, it avoids including specific information that only applies in certain interface contexts, such as anything specific to the chosen transport protocol, the programming language, and so on.

Each new Layer Type defines what new metadata it provides to the overall model. A Layer Type Specification may be written as a human-readable document, but is often provided as a “YAML schema” type of file, that can be used to programatically validate layer input against formal rules.

Layers do not always need to add new types of information. It is possible to ‘overlay’ files that use the same schema as an original file (such as the ifex-idl), for the purpose of adding some details that were not yet defined, removing nodes, or even redefining/changing the definition of some things defined in the original file.

In other words, tools are expected to be able to process the layer types that are relevant for the task at hand, but furthermore, some tools are expected to process multiple files of the same type and to merge their contents according to predefined rules. Conflicting information could for example be handled by writing a warning, or the tool may stipulate that the last provided layer file to takes precedence over previous definitions. (Refer to detailed documentation for each tool).

An example of this:

File: comfort-service.yml

name: comfort
  typedefs:
    - name: movement_t
      datatype: int16
      min: -1000
      max: 1000
      description: The movement of a seat component

File: redefine-movement-type.yml

name: comfort
  typedefs:
    - name: movement_t
      datatype: int8 # Replaces int16 of the original type

Tools that combine this information will end up processing a combined YAML structure that looks like this, in this case following the principle that the last-definition takes precedence.

name: comfort
  typedefs:
    - name: movement_t
      datatype: int8 # (Replaced datatype)
      min: -1000
      max: 1000
      description: The movement of a seat component

Extending the interface model by mimicking the structure

Most layers*** will follow the same hierarchical structure as the original interface definiton written in the IFEX Core IDL. If a layer object list element (e.g. events) is also defined in the IFEX file, the IFEX’s list will traversed recursively and extended by the deployment file’s corresponding list.

** For more information on layer types and different strategies, refer to the developers manual.

In this example, an overlay is used to add a new parameter to an existing interface. This is a bit of a peculiar case, but some development strategies may use this to more clearly local modifications on top of a standard interface description - such as an industry standard API. Separating it into an overlay makes it more explicit, but this should be understood as an example rather than a design guideline.

File: comfort-service.yml

name: comfort
events:
  - name: seat_moving
    description:  The event of a seat starting or stopping movement
    in:
      - name: status
        datatype: uint8
      - name: row
        datatype: uint8

File: add_seat_moving_in_parameter.yml

name: comfort
events:
- name: seat_moving: 
    in:
      - name: extended_status_text
        datatype: string

The combined structure to be processed will look like the following: (We show it here using YAML syntax, but this combined representation might only exist only inside the tool memory, after reading more than one input file.)

name: comfort
events:
  - name: seat_moving
    description:  The event of a seat starting or stopping movement
    in:
      - name: status
        datatype: uint8
      - name: row
        datatype: uint8
      - name: extended_status_text
        datatype: string

Note: There is not a fixed list of layer types – while some might be agreed upon in a community and documented within the IFEX project, the capability is also there to allow extensions that have not yet been envisioned. Some aspects of API and system design might be unique to one company’s way of working, and therefore defined and used only there.

DEPLOYMENT LAYER

Deployment layer, a.k.a. Deployment Model files, is a specialization of the general layers concept. This terminology is used to indicate a type of layer that in adds additional metadata that is directly related to the interface described in the IDL. It is information needed to process, or interpret, IFEX interface files in a particular target environment.

An example of deployment file data is a D-Bus interface specification to be used for a namespace, or a SOME/IP method ID to be used for a method call.

By separating the extension data into their own deployment files the core IFEX specification can be kept independent of deployment details such as network protocols and topology.

An example of a IFEX file sample and a deployment file extension to that sample is given below:

File: comfort-service.yml

name: comfort
namespaces:
  - name: seats
    description: Seat interface and datatypes.

    structs: ...
    methods: ...
   ...

File: comfort-dbus-deployment.yml

name: comfort
namespaces: 
  - name: seats
    dbus_interface: com.genivi.cabin.seat.v1

The combined YAML structure to be processed will look like this:

name: comfort
namespaces: 
  - name: seats
    description: Seat interface and datatypes.
    dbus_interface: com.genivi.cabin.seat.v1

    structs: ...
    methods: ...

The semantic difference between a regular IFEX file included by an includes list object and a deployment file is that the deployment file can follow a different specification/schema and add keys that are not allowed in the plain IDL layer. In the example above, the dbus_interface key-value pair can only be added in a deployment file since dbus_interface is not a part of the regular IFEX IDL file syntax.

More Layer Information

For more information about Layer Types and Layer design, refer to the corresponding chapter in the developers-manual.md


IFEX FILE SYNTAX, SEMANTICS AND STRUCTURE

A Vehicle Service Catalog is stored in one or more YAML files. The root of each YAML file is assumed to be a namespace object and needs to contain at least a name key, and, optionally, a description. In addition to this other namespaces, includes, datatypes, methods, events, and properties can be specified.

A complete IFEX file example is given below:

NOTE: This example might be outdated

---
name: comfort
major_version: 2
minor_version: 1
description: A collection of interfaces pertaining to cabin comfort.

# Include generic error enumeration to reside directly
# under comfort namespace
includes:
  - file: vsc-error.yml
    description: Include standard VSC error codes used by this namespace

namespaces:
  - name: seats
    description: Seat interface and datatypes.

    typedefs:
      - name: movement_t
        datatype: uint16
  
    structs:
      - name: position_t
        description: The position of the entire seat
        members:
          - name: base
            datatype: movement_t
            description: The position of the base 0 front, 1000 back
    
          - name: recline
            datatype: movement_t
            description: The position of the backrest 0 upright, 1000 flat
    
    enumerations:
      - name: seat_component_t
        datatype: uint8
        options:
          - name: base
            value: 0
          - name: recline
            value: 1
    
    methods:
      - name: move
        description: Set the desired seat position
        in: 
          - name: seat
            description: The desired seat position
            datatype: movement.seat_t
    
    
    events:
      - name: seat_moving
        description: The event of a seat beginning movement
        in:
          - name: status
            description: The movement status, moving (1), not moving (0)
            datatype: uint8
    
    properties:
      - name: seat_moving
        description: Specifies if a seat is moving or not
        type: sensor
        datatype: uint8

The following chapters specifies all YAML objects and their keys supported by IFEX. The “Lark grammar” specification refers to the Lark grammar that can be found here. The terminals used in the grammar (LETTER, DIGIT, etc) are imported from common.lark


NODE TYPES

The chapters that follow this one specify the node types for the core interface language/model. They are generated from a “source of truth” which is the actual python source code of ifex_ast.py. This means that while the examples are free-text and may need manual updating, the list of fields and optionality should always match the behavior of the tool(s). => Always trust the tables over the examples, and report any discrepancies.


Namespace

Dataclass used to represent IFEX Namespace.

A namespace is a logical grouping of other objects, allowing for separation of datatypes, methods, events, and properties into their own spaces that do not interfere with identically named objects in other namespaces. Namespaces can be nested inside other namespaces to an arbitrary depth, building up a scalable namespace tree. Namespaces can be reused either locally in a single file via YAML anchors, or across YAML files using the includes object. The root of a YAML file is assumed to be a namespaces object, and can contain the keys listed below.

A namespace example is given below.

namespaces:
  - name: seats
    major_version: 1
    minor_version: 3
    description: Seat interface and datatypes.

Mandatory fields for Namespace:

Field Name Contents
name A single str

Optional fields for Namespace:

Field Name Contents
description A single str
major_version A single int
minor_version A single int
version_label A single str
events A list of Events
methods A list of Methods
typedefs A list of Typedefs
includes A list of Includes
structs A list of Structs
enumerations A list of Enumerations
properties A list of Propertys
namespaces A list of Namespaces
interface A single Interface

Event

Dataclass used to represent IFEX Event.

Each events list object specifies a fire-and-forget call, executed by zero or more subscribing instances, that does not return a value. Execution is best effort to UDP level with server failures not being reported.

A events sample list object is given below:

events:
  - name: seat_moving
    description: Signal that the seat has started or stopped moving

    in:
      - name: status
        description: The movement status, moving (1), not moving (0)
        datatype: boolean

      - name: row
        description: The row of the seat
        datatype: uint8

Mandatory fields for Event:

Field Name Contents
name A single str

Optional fields for Event:

Field Name Contents
description A single str
input A list of Arguments

Argument

Dataclass used to represent IFEX method Argument.

methods:
  - name: current_position
    input:
      - name: row
        description: The desired seat to row query
        datatype: uint8
        range: $ < 10 and $ > 2

Mandatory fields for Argument:

Field Name Contents
name A single str
datatype A single str

Optional fields for Argument:

Field Name Contents
description A single str
arraysize A single int
range A single str

Method

Dataclass used to represent IFEX Event.

Each methods list object specifies a method call, executed by a single server instance, that optionally returns a value. Execution is guaranteed to TCP level with server failure being reported.

A methods sample list object is given below:

methods:
  - name: current_position
    description: Get the current position of a seat

    input:
      - name: row
        description: The desired seat to row query
        datatype: uint8
        range: $ < 10 and $ > 2

      - name: index
        description: The desired seat index to query
        datatype: uint8
        range: $ in_interval(1,4)

    output:
      - name: seat
        description: The state of the requested seat
        datatype: seat_t

    errors:
      - datatype: .stdvsc.error_t
        range: $ in_set("ok", "in_progress", "permission_denied")

Mandatory fields for Method:

Field Name Contents
name A single str

Optional fields for Method:

Field Name Contents
description A single str
errors A list of Errors
input A list of Arguments
output A list of Arguments
returns A list of Arguments

Error

Dataclass used to represent a IFEX method error.

The optional error element defines an error value to return. Note that the concept allows for multiple Errors. This is easy to misunderstand: It is not only multiple different error values as you are used to from most programming environments. Multiple value choices can be handled by a single Enumeration error type. The concept also allows multiple independent return parameters, each having their own data type (and name).

The purpose of this is to be able to separate different error categories and to define them independently using layers. For example, a method is likely to have a collection of business-logic errors defined in its interface description and represented by one enum type. At a later time transport-protocol specific errors can be added when the interface is deployed over a certain protocol, and that error parameter has a different name and a different type (enumeration or otherwise).

Error elements are returned in addition to any out elements specified for the method call.

Please see the methods sample code above for an example of how error parameter lists are used If no error element is specified, no specific error code is returned. Results may still be returned as an out parameter`

methods:
  - name: current_position
    description: Get the current position of a seat

    errors:
      - name: "progress"
        datatype: .stdvsc.error_t
        range: $ in_set("ok", "in_progress", "permission_denied")
      - <possibly additional error definition>

Mandatory fields for Error:

Field Name Contents
datatype A single str

Optional fields for Error:

Field Name Contents
name A single str
description A single str
arraysize A single int
range A single str

Typedef

Dataclass used to represent IFEX Typedef.

A Typedef is an alias to an existing fundamental type or defined type, including structs, enumerators, etc. It can also be used to name and define a variant type. The new data type name can be used in the definition of other datatypes, method- and event-parameters, and properties.

A typedefs list object example is given below:

typedefs:
  - name: movement_t
    datatype: int16
    min: -1000
    max: 1000
    description: The movement of a seat component

Variant types

The fields datatype and datatypes are mutually exclusive - only one of them may have a value. The field datatypes is used to specify multiple types associated with this type name. Doing this makes it a variant type. It is also possible to define a variant type using the variant<a,b,c>-style syntax in any location requiring a datatype, but the ability to specify it as a list can be more useful if the number of types is large. Handling this as a list can also allow Layers to extend a variant type more conveniently.

typedefs:
  - name: StringOrStruct
    datatypes:
      - string
      - MyStruct
      - OtherStruct
    description: The Thing, represented in one of 3 ways.

NOTE! The fields datatype and datatypes are defined as optional in the language model, but one of them must be defined.

Mandatory fields for Typedef:

Field Name Contents
name A single str

Optional fields for Typedef:

Field Name Contents
datatype A single str
datatypes A list of strs
description A single str
arraysize A single int
min A single int
max A single int

Include

Dataclass used to represent IFEX Include.

Each includes list object specifies a IFEX YAML file to be included into the namespace hosting the includes list. The included file’s structs, typedefs, enumerators, methods, events, and properties lists will be appended to the corresponding lists in the hosting namespace.

A includes sample list object is given below:

namespaces:
  - name: top_level_namespace
    includes
    - file: vsc-error.yml;
      description: Global error used by methods in this file

Mandatory fields for Include:

Field Name Contents
file A single str

Optional fields for Include:

Field Name Contents
description A single str

Struct

Dataclass used to represent IFEX Struct.

Each structs list object specifies an aggregated data type. The new data type can be used by other datatypes, method & event parameters, and properties.

A structs list object example is shown below:

structs:
  - name: position_t
      description: The complete position of a seat
      members:
      - name: base
          datatype: movement_t
          description: The position of the base 0 front, 1000 back

      - name: recline
          datatype: movement_t
          description: The position of the backrest 0 upright, 1000 flat

Mandatory fields for Struct:

Field Name Contents
name A single str

Optional fields for Struct:

Field Name Contents
description A single str
members A list of Members

Member

Dataclass used to represent IFEX Struct Member.

Each members list object defines an additional member of the struct. Each member can be of a fundamental or defined/complex datatype.

Please see the struct sample code above for an example of how members list objects are used.

structs:
  - name: position_t
    description: The complete position of a seat
    members:
      - name: base
        datatype: movement_t
        description: The position of the base 0 front, 1000 back

Mandatory fields for Member:

Field Name Contents
name A single str
datatype A single str

Optional fields for Member:

Field Name Contents
description A single str
arraysize A single int

Enumeration

Dataclass used to represent IFEX Enumeration.

Each enumerations list object specifies an enumerated list (enum) of options, where each option can have its own integer value. The new data type defined by the enum can be used by other datatypes, method & event parameters, and properties.

A enumerations example list object is given below:

enumerations:
  - name: seat_component_t
    datatype: uint8
    options:
      - name: base
        value: 0

      - name: cushion
        value: 1

Mandatory fields for Enumeration:

Field Name Contents
name A single str
datatype A single str
options A list of Options

Optional fields for Enumeration:

Field Name Contents
description A single str

Option

Dataclass used to represent IFEX Enumeration Option.

Each options list object adds an option to the enumerator.

Please see the enumerations sample code above for an example of how options list objects are used.

options:
  - name: base
    value: 0
    description: description of the option value

Mandatory fields for Option:

Field Name Contents
name A single str
value A single Any

Optional fields for Option:

Field Name Contents
description A single str

Property

Dataclass used to represent IFEX Property.

Each properties list object specifies a shared state object that can be read and set, and which is available to all subscribing entities. A properties sample list object is given below, together with a struct definition:

properties:
  - name: dome_light_status
    description: The dome light status
    datatype: dome_light_status_t

Mandatory fields for Property:

Field Name Contents
name A single str
datatype A single str

Optional fields for Property:

Field Name Contents
description A single str
arraysize A single int

Interface

Dataclass used to represent IFEX Interface

An Interface is a container of other items in a similar way as a Namespace, but it does not introduce a new namespace level. Its purpose is to explicitly define what shall be considered part of the exposed as a “public API” in the output.

Note that as with all IFEX concepts, the way it is translated into target code could be slightly varying, and controlled by the target mapping and deployment information, but all mappings shall strive to stay close to the IFEX expected behavior. Thus, the Interface section would omit for example internal helper-methods and type definitions that are only used internally, while those definitions might still need to be in the parent Namespace for the code generation to work.

An Interface can contain all the same child node types as Namespace can, except for additional (nested) Interfaces. As mentioned, it does NOT introduce an additional namespace level regarding the visibility/reacahbility of the items. Its contents is not hidden from other Namespace or Interface definitions within the same Namespace. To put it another way, from visibility/reachability point of view all the content inside an interface container is part of the “parent” Namespace that the Interface object is placed in. Of course, if additional nested namespaces are placed below the Interface node, those Namespaces introduce new namespace levels, as usual.

Only one Interface can be defined per Namespace, and it cannot include other Interfaces

An Interface example is given below.

namespaces:
  - name: seats
    major_version: 1
    minor_version: 3
    description: Seat interface and datatypes.
    interface:
      typedefs:
        - ...
      methods:
        - ...
      properties:
        - ...

Mandatory fields for Interface:

Field Name Contents
name A single str

Optional fields for Interface:

Field Name Contents
description A single str
major_version A single int
minor_version A single int
version_label A single str
events A list of Events
methods A list of Methods
typedefs A list of Typedefs
includes A list of Includes
structs A list of Structs
enumerations A list of Enumerations
properties A list of Propertys
namespaces A list of Namespaces