This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Manual Vehicle Model Creation

Learn how to manually create a vehicle model to access vehicle data or execute remote procedure calls.

This tutorial will show you how to:

  • Create a Vehicle Model
  • Add a Vehicle Service to the Vehicle Model
  • Distribute your Python Vehicle Model

Create a Vehicle Model from VSS specification

A Vehicle Model can be generated from a COVESA Vehicle Signal Specification (VSS). VSS introduces a domain taxonomy for vehicle signals, in the sense of classical attributes, sensors and actuators with the raw data communicated over vehicle buses and data. The Velocitas vehicle-model-generator creates a Vehicle Model from the given specification and generates a package for use in Vehicle App projects.

Follow the steps to generate a Vehicle Model.

  1. Clone the vehicle-model-generator repository in a container volume.

  2. In this container volume, clone the vehicle-signal-specification repository and if required checkout a particular branch:

    git clone https://github.com/COVESA/vehicle_signal_specification
    
    cd vehicle_signal_specification
    git checkout <branch-name>
    

    In case the VSS vspec doesn’t contain the required signals, you can create a vspec using the VSS Rule Set .

  3. Execute the command

    python3 gen_vehicle_model.py -I ./vehicle_signal_specification/spec ./vehicle_signal_specification/spec/VehicleSignalSpecification.vspec -l <lang> -T sdv_model -N sdv_model
    

    or if you want to generate it from a .json file

    python3 gen_vehicle_model.py <path_to_your_json_file> -l <lang> -T sdv_model
    

    Depending on the value of lang, which can assume the values python and cpp, this creates a sdv_model directory in the root of repository along with all generated source files for the given programming language.

    Here is an overview of what is generated for every available value of lang:

    lang output
    python Python sources and a setup.py ready to be used as Python package
    cpp C++ sources, headers and a CMakeLists.txt ready to be used as a CMake project

    To have a custom model name, refer to README of vehicle-model-generator repository.

  4. For Python: Change the version of package in setup.py manually (defaults to 0.1.0).

  5. Now the newly generated sdv_model can be used for distribution. (See Distributing your Vehicle Model )

Create a Vehicle Model Manually

Alternative to the generation from a VSS specification you could create the Vehicle Model manually. The following sections describing the required steps.

Distributing your Vehicle Model

Once you have created your Vehicle Model either manually or via the Vehicle Model Generator, you need to distribute your model to use it in an application. Follow the links below for language specific tutorials on how to distribute your freshly created Vehicle Model.

Further information

1 - C++ Manual Vehicle Model Creation

Learn how to create a Vehicle Model manually for C++

Not yet done for C++

2 - Python Manual Vehicle Model Creation

Learn how to create a Vehicle Model manually for Python

Setup a Python Package manually

A Vehicle Model should be defined in its own Python Package. This allows to distribute the Vehicle Model later as a standalone package and to use it in different Vehicle App projects.

The name of the Vehicle Model package will be my_vehicle_model for this walkthrough.

  1. Start Visual Studio Code

  2. Select File > Open Folder (File > Open… on macOS) from the main menu.

  3. In the Open Folder dialog, create a my_vehicle_model folder and select it. Then click Select Folder (Open on macOS).

  4. Create a new file setup.py under my_vehicle_model:

    from setuptools import setup
    
    setup(name='my_vehicle_model',
        version='0.1',
        description='My Vehicle Model',
        packages=['my_vehicle_model'],
        zip_safe=False)
    

    This is the Python package distribution script.

  5. Create an empty folder my_vehicle_model under my_vehicle_model.

  6. Create a new file __init__.py under my_vehicle_model/my_vehicle_model.

At this point the source tree of the Python package should look like this:

my_vehicle_model
├── my_vehicle_model
│   └── __init__.py
└── setup.py

To verify that the package is created correctly, install it locally:

pip3 install .

The output of the above command should look like this:

Defaulting to user installation because normal site-packages is not writeable
Processing /home/user/projects/my-vehicle-model
Preparing metadata (setup.py) ... done
Building wheels for collected packages: my-vehicle-model
Building wheel for my-vehicle-model (setup.py) ... done
Created wheel for my-vehicle-model: filename=my_vehicle_model-0.1-py3-none-any.whl size=1238 sha256=a619bc9fbea21d587f9f0b1c1c1134ca07e1d9d1fdc1a451da93d918723ce2a2
Stored in directory: /home/user/.cache/pip/wheels/95/c8/a8/80545fb4ff73c974ac1716a7bff6f7f753f92022c41c2e376f
Successfully built my-vehicle-model
Installing collected packages: my-vehicle-model
Successfully installed my-vehicle-model-0.1

Finally, uninstall the package again:

pip3 uninstall my_vehicle_model

Add Vehicle Models manually

  1. Install the Python Vehicle App SDK:

    pip3 install git+https://github.com/eclipse-velocitas/vehicle-app-python-sdk.git
    

    The output of the above command should end with:

    Successfully installed sdv-x.y.z
    

    Now it is time to add some Vehicle Models to the Python package. At the end of this section you will have a Vehicle Model, that contains a Cabin model, a Seatmodel and has the following tree structure:

     Vehicle
     └── Cabin
         └── Seat (Row, Pos)
    
  2. Create a new file Seat.py under my_vehicle_model/my_vehicle_model:

    from sdv.model import Model
    
    class Seat(Model):
    
        def __init__(self, parent):
            super().__init__(parent)
            self.Position = DataPointFloat("Position", self)
    

    This creates the Seat model with a single data point of type float named Position.

  3. Create a new file Cabin.py under my_vehicle_model/my_vehicle_model:

    from sdv.model import Model
    
      class Cabin(Model):
          def __init__(self, parent):
              super().__init__(parent)
              self.Seat = SeatCollection("Seat", self)
    
      class SeatCollection(Model):
          def __init__(self, name, parent):
              super().__init__(parent)
              self.name = name
              self.Row1 = self.RowType("Row1", self)
              self.Row2 = self.RowType("Row2", self)
    
          def Row(self, index: int):
              if index < 1 or index > 2:
                  raise IndexError(f"Index {index} is out of range")
              _options = {
                  1 : self.Row1,
                  2 : self.Row2,
              }
              return _options.get(index)
    
          class RowType(Model):
              def __init__(self, name, parent):
                  super().__init__(parent)
                  self.name = name
                  self.Pos1 = Seat("Pos1", self)
                  self.Pos2 = Seat("Pos2", self)
                  self.Pos3 = Seat("Pos3", self)
    
              def Pos(self, index: int):
                  if index < 1 or index > 3:
                      raise IndexError(f"Index {index} is out of range")
                  _options = {
                      1 : self.Pos1,
                      2 : self.Pos2,
                      3 : self.Pos3,
                  }
                  return _options.get(index)
    

    This creates the Cabin model, which contains a set of six Seat models, referenced by their names or by rows and positions:

    • row=1, pos=1
    • row=1, pos=2
    • row=1, pos=3
    • row=2, pos=1
    • row=2, pos=2
    • row=2, pos=3
  4. Create a new file vehicle.py under my_vehicle_model/my_vehicle_model:

    from sdv.model import Model
    from my_vehicle_model.Cabin import Cabin
    
    
    class Vehicle(Model):
        """Vehicle model"""
    
        def __init__(self, name):
            super().__init__()
            self.name = name
            self.Speed = DataPointFloat("Speed", self)
            self.Cabin = Cabin("Cabin", self)
    
    vehicle = Vehicle("Vehicle")
    

The root model of the Vehicle Model tree should be called Vehicle by convention and is specified, by setting parent to None. For all other models a parent model must be specified as the 2nd argument of the Model constructor, as can be seen by the Cabin and the Seat models above.

A singleton instance of the Vehicle Model called vehicle is created at the end of the file. This instance is supposed to be used in the Vehicle Apps. Creating multiple instances of the Vehicle Model should be avoided for performance reasons.

Add a Vehicle Service

Vehicle Services provide service interfaces to control actuators or to trigger (complex) actions. E.g. they communicate with the vehicle internal networks like CAN or Ethernet, which are connected to actuators, electronic control units (ECUs) and other vehicle computers (VCs). They may provide a simulation mode to run without a network interface. Vehicle Services may feed data to the Databroker and may expose gRPC endpoints, which can be invoked by Vehicle Apps over a Vehicle Model.

In this section, we add a Vehicle Service to the Vehicle Model.

  1. Create a new folder proto under my_vehicle_model/my_vehicle_model.

  2. Copy your proto file under my_vehicle_model/my_vehicle_model/proto

    As example you could use the protocol buffers message definition seats.proto provided by the KUKSA services which describes a seat control service .

  3. Install the grpcio tools including mypy types to generate the Python classes out of the proto-file:

    pip3 install grpcio-tools mypy_protobuf
    
  4. Generate Python classes from the SeatService message definition:

    python3 -m grpc_tools.protoc -I my_vehicle_model/proto --grpc_python_out=./my_vehicle_model/proto --python_out=./my_vehicle_model/proto --mypy_out=./my_vehicle_model/proto my_vehicle_model/proto/seats.proto
    

    This creates the following gRPC files under the proto folder:

    • seats_pb2.py
    • seats_pb2_grpc.py
    • seats_pb2.pyi
  5. Create the SeatService class and wrap the gRPC service:

    from sdv.model import Service
    
    from my_vehicle_model.proto.seats_pb2 import (
        CurrentPositionRequest,
        MoveComponentRequest,
        MoveRequest,
        Seat,
        SeatComponent,
        SeatLocation,
    )
    from my_vehicle_model.proto.seats_pb2_grpc import SeatsStub
    
    
    class SeatService(Service):
        "SeatService model"
    
        def __init__(self):
            super().__init__()
            self._stub = SeatsStub(self.channel)
    
        async def Move(self, seat: Seat):
            response = await self._stub.Move(MoveRequest(seat=seat), metadata=self.metadata)
            return response
    
        async def MoveComponent(
            self,
            seatLocation: SeatLocation,
            component: SeatComponent,
            position: int,
        ):
            response = await self._stub.MoveComponent(
                MoveComponentRequest(
                    seat=seatLocation,
                    component=component,  # type: ignore
                    position=position,
                ),
                metadata=self.metadata,
            )
            return response
    
        async def CurrentPosition(self, row: int, index: int):
            response = await self._stub.CurrentPosition(
                CurrentPositionRequest(row=row, index=index),
                metadata=self.metadata,
            )
            return response
    

    Some important remarks about the wrapping SeatService class shown above:

    • The SeatService class must derive from the Service class provided by the Python SDK.
    • The SeatService class must use the gRPC channel from the Service base class and provide it to the _stub in the __init__ method. This allows the SDK to manage the physical connection to the gRPC service and use service discovery of the middleware.
    • Every method needs to pass the metadata from the Service base class to the gRPC call. This is done by passing the self.metadata argument to the metadata of the gRPC call.

3 - Vehicle Model Distribution

Learn how to distribute a Vehicle Model.

3.1 - C++ Vehicle Model Distribution

Learn how to distribute a Vehicle Model written in C++.

Now that you have created your own Vehicle Model, we can distribute it to make use of it in Vehicle Apps.

Copying the folder to your Vehicle App repo

The easiest way to get started quickly is to copy the created model, presumably stored in vehicle_model into your Vehicle App repository to use it. To do so, simply copy and paste the directory into the <sdk_root>/app directory and replace the existing model.

Using a git submodule

A similar approach to the one above but a bit more difficult to set up is to create a git repository for the created model. The advantage of this approach is that you can share the same model between multiple Vehicle Apps without any manual effort.

  1. Create a new git repository on i.e. Github
  2. Clone it locally, add the created vehicle_model folder to the git repository
  3. Commit everything and push the branch

In your Vehicle App repo, add a new git submodule via

git submodule add <checkout URL of your new repo> app/vehicle_model
git submodule init

Now you are ready to develop new Vehicle Apps with your custom Vehicle Model!

3.2 - Python Vehicle Model Distribution

Learn how to distribute a Vehicle Model written in Python.

Now you a have a Python package containing your first Python Vehicle Model and it is time to distribute it. There is nothing special about the distribution of this package, since it is just an ordinary Python package. Check out the Python Packaging User Guide to learn more about packaging and package distribution in Python.

Distribute to single Vehicle App

If you want to distribute your Python Vehicle Model to a single Vehicle App, you can do so by copying the entire folder my_vehicle_model under the /app/src folder of your Vehicle App repository and treat it as a sub-package of the Vehicle App.

  1. Create a new folder my_vehicle_model under /app/src in your Vehicle App repository.
  2. Copy the my_vehicle_model folder to the /app/src folder of your Vehicle App repository.
  3. Import the package my_vehicle_model in your Vehicle App:
from <my_app>.my_vehicle_model import vehicle

...

my_app = MyVehicleApp(vehicle)

Distribute inside an organization

If you want to distribute your Python Vehicle Model inside an organization and use it to develop multiple Vehicle Apps, you can do so by creating a dedicated Git repository and copying the files there.

  1. Create new Git repository called my_vehicle_model

  2. Copy the content under my_vehicle_model to the repository.

  3. Release the Vehicle Model by creating a version tag (e.g., v1.0.0).

  4. Install the Vehicle Model package to your Vehicle App:

    pip3 install git+https://github.com/<yourorg>/my_vehicle_model.git@v1.0.0
    
  5. Import the package my_vehicle_model in your Vehicle App and use it as shown in the previous section.

Distribute publicly as open source

If you want to distribute your Python Vehicle Model publicly, you can do so by creating a Python package and distributing it on the Python Package Index (PyPI) . PyPi is a repository of software for the Python programming language and helps you find and install software developed and shared by the Python community. If you use the pip command, you are already using PyPI.

Detailed instructions on how to make a Python package available on PyPI can be found here .