This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
Vehicle Model Creation
Learn how creation of vehicle models work and how to adapt it to your needs.
A Vehicle Model makes it possible to easily get vehicle data from the KUKSA Databroker and to execute remote procedure calls over gRPC against Vehicle Services and other Vehicle Apps. It is generated from the underlying semantic models based e.g. on the
COVESA Vehicle Signal Specification (VSS)
. The model is generated for a concrete programming language as a graph-based, strongly-typed, intellisense-enabled library providing vehicle abstraction “on code level”.
By default our app templates now generate the vehicle model during the devContainer initialization - managed by the Velocitas life cycle management. The respective VSS-based model source is referenced in the app manifest allowing to freely choose the model being used in your project. You will find more details about this in section
Automated Model Lifecycle
.
The previous approach, using pre-generated model repositories, is deprecated as of now. But is still available and is described in section
Manual Vehicle Model Creation
. Please be aware, that you would either have to use template versions before the above mentioned release, or you need to adapt the newer versions of the template using the old approach.
1 - Automated Vehicle Model Lifecycle
Learn how to refer a model source and how the automated model lifecycle is working.
Info
This article describes our new model lifecycle approach released on Friday, 2023-03-03. With that, the model is now automatically generated with the instantiation of the devContainer. It is generated from the vehicle model source file referenced in the AppManifest.
For the time being, the integration of services is not supported by the new approach.
The previous approach, using pre-generated model repositories, is now deprecated. But it is still available and
described here
.
This tutorial will show you how:
- the vehicle API used as the source to generate the model is referenced in the app manifest,
- the automatic generation of the model works,
- you can trigger manual recreation of the model (after adding extensions to the API required by your project)
How to Reference a Model Specification
The model specification defines the vehicle API to be used by your project. It is referenced in the AppManifest.json
via a URI or local file path like this:
"vehicleModel": {
"src": "<https://github.com/COVESA/vehicle_signal_specification/releases/download/v3.0/vss_rel_3.0.json>"
}
"vehicleModel": {
"src": "./my_model/my_model.json"
}
Info
The reference must point to a JSON file containing the model specification as VSS vspec. References to a VSS .vspec
file hierarchy are not supported as of now.
Model Creation
The generation of the model is taking place:
- through a
onPostInit hook
when
velocitas init
is called:
- when you trigger the VS Code task
(Re-)generate vehicle model
explicitly.
The model generation is a three step process:
- The model generator is installed as a Python package (if not already present)
- The referenced model specification is downloaded (if no local reference)
- The model code is generated and installed.
The model is generated using our
Velocitas vehicle-model-generator
.
The used version and also the repository of the generator can be altered via the variables
section of the project configuration in the .velocitas.json
.
The default values for those are defined in the
manifest.json
of the
devContainer setup package
.
Also, the target folder for the generated model source code is specified here:
{
"variables": {
"modelGeneratorGitRepo": "https://github.com/eclipse-velocitas/vehicle-model-generator.git",
"modelGeneratorGitRef": "v0.3.0",
"generatedModelPath": "./gen/vehicle_model"
}
}
In Python template based projects the generated model is finally installed in the site-packages folder, while in C++ projects it is made available as a CMake include folder.
2 - Manual Vehicle Model Creation
Learn how to manually create a vehicle model to access vehicle data or execute remote procedure calls.
Info
With the release of our new
model lifecycle approach
on Friday, 2023-03-03, the model is now automatically generated with the instantiation of the devContainer from a model source referenced in the app manifest.
The approach described here, using pre-generated model repositories, is deprecated as of now. But it is still available and must be used if you need access to vehicle services. Please be aware, that you would either have to use template versions before the above mentioned release, or you need to adapt the newer versions of the template using the old approach.
This tutorial will show you how to:
- Create a Vehicle Model
- Add a Vehicle Service to the Vehicle Model
- Distribute your Python Vehicle Model
Note
A Vehicle Model should be defined in its own package. This makes it possible to distribute the Vehicle Model later as a standalone package and to use it in different Vehicle App projects.
The creation of a new vehicle model is only required if the vehicle signals (like sensors and actuators) defined in the current version of the
COVESA Vehicle Signal Specification
(VSS) is not sufficient for the definition of your vehicle API. Otherwise you could use the default vehicle model we already generated for you, see
Python Vehicle Model
and
C++ 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.
-
Clone the
vehicle-model-generator
repository in a container volume.
-
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
.
-
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.
-
For Python: Change the version of package in setup.py
manually (defaults to 0.1.0).
-
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.
2.1 - C++ Manual Vehicle Model Creation
Learn how to create a Vehicle Model manually for C++
Not yet done for C++
2.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.
-
Start Visual Studio Code
-
Select File > Open Folder (File > Open… on macOS) from the main menu.
-
In the Open Folder dialog, create a my_vehicle_model
folder and select it. Then click Select Folder (Open on macOS).
-
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.
-
Create an empty folder my_vehicle_model
under my_vehicle_model
.
-
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:
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
-
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 Seat
model and has the following tree structure:
Vehicle
└── Cabin
└── Seat (Row, Pos)
-
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
.
-
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
-
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.
-
Create a new folder proto
under my_vehicle_model/my_vehicle_model
.
-
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
.
-
Install the grpcio tools including mypy types to generate the Python classes out of the proto-file:
pip3 install grpcio-tools mypy_protobuf
-
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
-
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.
2.3 - Vehicle Model Distribution
Learn how to distribute a Vehicle Model.
2.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.
- Create a new git repository on i.e.
Github
- Clone it locally, add the created
vehicle_model
folder to the git repository
- 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!
2.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.
- Create a new folder
my_vehicle_model
under /app/src
in your Vehicle App repository.
- Copy the
my_vehicle_model
folder to the /app/src
folder of your Vehicle App repository.
- 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.
-
Create new Git repository called my_vehicle_model
-
Copy the content under my_vehicle_model
to the repository.
-
Release the Vehicle Model by creating a version tag (e.g., v1.0.0
).
-
Install the Vehicle Model package to your Vehicle App:
pip3 install git+https://github.com/<yourorg>/my_vehicle_model.git@v1.0.0
-
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
.