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

Return to the regular view of this page.

Vehicle Applications

This chapter contains use cases and examples for SDV applications.

Some of these example are taken from the following sources:

1 - Seat Adjuster

The seat adjuster is an example to showcase how to create a vehicle application which senses and actuates signals in the vehicle for Eclipse Leda with help of Eclipse Velocitas and Eclipse Kuksa . The aim is not to build the best available seat adjuster application possible but to show how one can use the applied technologies to build a companion app for the interaction with a vehicle. If you are new to the concepts around Eclipse SDV and the mentioned projects we recommend to read the SDV Tutorial first.

Description

The idea of the seat adjuster application is to have a custom application to move the driver seat to positions defined in a driver profile hosted by a third-party web service.

Leda Seat Adjuster Use Case

The setup contains the following components:

  • Cloud or mobile trigger: not part of the Leda image, but we simulate it by issuing MQTT messages
  • Seat Adjuster : Developed with Eclipse Velocitas to be deployed by user
  • Eclipse Kuksa.val - KUKSA Databroker (pre-installed with Eclipse Leda)
  • Mock Service: Example provider for Eclipse Kuksa.VAL which mocks the behavior of the vehicle.
  • Seat ECU and the separate Seat Motor hardware can be emulated using a virtual CAN-Bus, which is beyond the scope of this guide.

In the following paragraphs, we next introduce the architecture and the assumed data flow before we explain the development and deployment steps. If you are more interested in the general development steps, you may directly jump to the develop seat adjuster.

1.1 - Architecture of Seat Adjuster

The seat adjuster application interacts with the vehicle through a Vehicle Abstraction Layer created by the KUKSA Databroker, which uses the Vehicle Signal Specification (VSS) to express the current value and in case of actuators also the desired state of the vehicle signal. By developing against the abstraction layer, the application becomes independent from the actual physical seat.

To control the position of the driver seat, the seat adjuster sets the target value of the Vehicle.Cabin.Seat.Row1.Pos1.Position signal in the KUKSA Databroker.

The architecture assumes so-called actuation providers that apply the changes to the actual vehicle as indicated in the target value, e.g., through interaction with the responsible ECUs. For this tutorial, we do not expect you to interface with an actual vehicle and thus abstract the vehicle by using the Kuksa.Val vehicle mock service. This vehicle mock service allows the definition of behavior toward the KUKSA Databroker like we would expect from the vehicle, for example, setting the current value after reacting to changes to the target value of a signal.

flowchart TB client[Client] anotherClient[Another Client] seatadjuster[Seat Adjuster] databroker[(KUKSA
Databroker)] mqttRequest[[MQTT topic
seatadjuster/setPosition/request]] mockservice[Provider: Mock Service] client -- "JSON Request: position, requestId" --> mqttRequest anotherClient -.-> mqttRequest mqttRequest --> seatadjuster seatadjuster -- "Set Target
Vehicle.Cabin.Seat.Row1.Pos1.Position
gRPC" --> databroker databroker <-- "Set Current
Vehicle.Cabin.Seat.Row1.Pos1.Position

Notify subscriber of changed target
Vehicle.Cabin.Seat.Row1.Pos1.Position" --> mockservice

As interface to the user, we assume a client that can, for example, be a local app in the infotainment domain with a user interface or an off-board application sending the request from a backend. Either way, the client controls our seat adjuster application through a JSON encoded message over MQTT using the topic setPosition/request.

An example request looks like this:

mosquitto_pub -t seatadjuster/setPosition/request 
    -m '{"position": 1000, "requestId": "12345"}'

The position parameter can be any value between 0 and 1000.

Getting Seat Position

When the seat moves, the provider gets this information, for example, from the seat ECU over the CAN-bus. We use the mock service as a provider and configure it to set the current value for the Vehicle.Cabin.Seat.Row1.Pos1.Position signal in the KUKSA Databroker after the target value for the signal changes.

To receive the changes to the seat position, the seat adjuster application already subscribed to the current value of the signals and thus gets notified. As a result, the seat adjuster constructs a JSON message and sends the new seat position to the MQTT-topic seatadjuster/currentPosition where any client can consume it.

The seat adjuster also sends responses to each request received at the MQTT-topic seatadjuster/request with a message to the MQTT-topic seatadjuster/response indicating whether it accepted the incoming request and set the target value or whether there was an error like the vehicle currently moving.

flowchart TD client[Client] anotherClient[Another Client] seatadjuster[Seat Adjuster] databroker[(KUKSA
Databroker)] mqttResponse[[MQTT topic
seatadjuster/setPosition/response]] mqttCurrent[[MQTT topic
seatadjuster/currentPosition]] mockservice[Provider: Mock Service] mockservice -- "Feed/Set
Vehicle.Cabin.Seat.Row1.Pos1.Position
(Sensor)" --> databroker databroker -- "Notify Subscriber
Vehicle.Cabin.Seat.Row1.Pos1.Position" --> seatadjuster seatadjuster -- "Once after processing
/setPosition/request
message" --> mqttResponse seatadjuster -- "While seat is moving" --> mqttCurrent mqttResponse -- "SetPosition Response
(Accepted / Error)" --> client mqttCurrent --> client mqttCurrent -.-> anotherClient mqttResponse -.-> anotherClient

The next step is to develop the seat adjuster with the help of Eclipse Velocitas.

1.2 - Develop Seat Adjuster

The following pages show how to execute the explained setup using Eclipse Velocitas and Eclipse Leda 0.1.0-M2:

On a high level, you need to perform the following steps described in more detail in this guide:

  1. Use the Eclipse Velocitas template repository to develop, build and deploy your version of the seat adjuster example.

  2. Run Eclipse Leda, for example, as container or with other options like QEMU, physical hardware, etc..

  3. Manage the Eclipse Kanto container runtime to deploy your seat adjuster application.

  4. Test the deployed setup by interacting with the seat adjuster to change the seat position.

Setup Eclipse Velocitas from template repository

We use Eclipse Velocitas to develop the seat application against the API of the Kuksa.val Databroker:

In the first step, you create a copy of the Eclipse Velocitas template repository. You create the copy by opening the repository in GitHub and selecting: Use this template -> Create a new repository. In the next step, you choose under which organization to place the created repository and whether it should be public. If you set the repository to private, you have to perform additional configuration steps mentioned below.

The template repository contains skeleton code for the application, test and release workflows for GitHub actions, and the configuration of a development environment in a container.

In addition to the Python template used in this guide, there is a C++ template available.

Execute Development Container in VSCode

You then checkout the repository on your development machine and open it in VSCode. From VSCode you can execute the development container (DevContainer) configured in the repository. For this to function, you need to install the following tools on your computer:

In the best case, VSCode may detect that the repository contains a DevContainer configuration and ask you whether to execute it. Alternatively, you can press F1 in VSCode and then enter DevContainers: Reopen in container to start the DevContainer. In either case, VSCode should reopen and then connect to the DevContainer. Because of the DevContainer, we do not need to make further modifications to the developer machine.

Develop the application

In the next step, we work on the actual application. You find the skeleton code in /app/src/main.py of the Eclipse Velocitas template and thus the DevContainer.

Eclipse Velocitas provides the code to realize the described seat adjuster application. One way to get the code is to use a pre-configured task in VSCode by pressing ‘F1’ and then typing ‘Run Task’.

As an alternative, you can check the latest version of the seat adjuster example code in the Eclipse Velocitas SDK repository in the Velocitas SDK directory.

There is a chance that the latest code for the seat-adjuster example in the main-branch of the Eclipse Velocitas SDK is a bit different compared to the code snippets below.

Let us walk through some lines of the code where the logic is in the vapp.py while the entry point is in main.py.

In the on_start(self) function we subscribe to any changes of the current value of the VSS signal Vehicle.Cabin.Seat.Row1.Pos1.Position in the KUKSA Databroker.

async def on_start(self):
        """Run when the vehicle app starts"""
        await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.subscribe(
            self.on_seat_position_changed
        )

When the seat position changes, the on_seat_position_changed function is triggered and publishes the new seat position value as MQTT-message to the topic seatadjuster/currentPosition.

 async def on_seat_position_changed(self, data: DataPointReply):
        response_topic = "seatadjuster/currentPosition"
        await self.publish_event(
            response_topic,
            json.dumps(
                {"position": data.get(self.Vehicle.Cabin.Seat.Row1.Pos1.Position).value}
            ),
        )

In addition, we have the function on_set_position_request_received which is triggered for every MQTT-message to the topic seatadjuster/setPosition/request.

 @subscribe_topic("seatadjuster/setPosition/request")
    async def on_set_position_request_received(self, data_str: str) -> None:
        data = json.loads(data_str)
        (...)
        vehicle_speed = (await self.Vehicle.Speed.get()).value

        position = data["position"]
        if vehicle_speed == 0:
                try:
                    await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.set(position)
        (...)

If the vehicle speed is zero, meaning that the vehicle does not move, we set the target value in the KUKSA Databroker for the position signal of the seat to the value requested in the incoming MQTT message.

The get() and set() functions are created based on a VSS model, and we assume that the KUKSA Databroker instance uses the same VSS model. In many cases, like the default Eclipse Leda, one may rely on the upstream VSS model, but some scenarios require further signals, e.g., by applying an overlay.

You can configure the used VSS model in the app/AppManifest.json file. This file includes an interface definition with the entry for the default vehicle_signal_interface. The VSS signals in this guide are part of VSS in version 3.0.0. The DevContainer generates the respective Eclipse Velocitas SDK based on this configuration. For more details see the Eclipse Velocitas Model Generator.

At this point, you may wonder how the application actually knows to which MQTT broker to connect and where to find the correct instance of the KUKSA Databroker. This service discovery is abstracted within the Velocitas SDK and involves the usage of so-called middleware to find and call the other components. As of writing this page, Eclipse Velocitas supports DAPR as middleware or uses the native approach to configure the correct address directly. The details of how to set and configure the used middleware are part of the deployment.

Signal Description: The signal indicates the seat position on the vehicle x-axis, where the value 0 is the frontmost position supported by the seat.

The value is the distance measured in millimeters.

The example implementation supports values between 0 and 1000 millimeters.

Note: This range of valid values is not reflected in the standard Vehicle Signal Specification. OEMs would overlay the VSS tree with actual Min/Max values, depending on the seat hardware available in the vehicle model.

Start Runtime in DevContainer to test application

You may skip the application testing since we already provide the example code. But it still makes sense to get a general idea of how to test and debug the code with the tooling in the DevContainer.

As mentioned before, the execution and, thus, the testing of the application requires a set of other components to be available like the KUKSA Databroker or an MQTT-broker. Furthermore, the deployment, the configuration, and the behavior of the application may change depending on the used (container) management solution. Therefore, Eclipse Velocitas allows the deployment of the required components and the application with the container management approach of choice inside the DevContainer.

The different runtimes are maintained in a separate Eclipse Velocitas repository. To start a runtime, we use the Eclipse Velocitas CLI:

velocitas exec runtime-kanto up

We use the Eclipse Kanto runtime here since we later deploy the seat adjuster application to Eclipse Leda, which uses Eclipse Kanto for container management.

Once the runtime is available, we add our application by executing:

velocitas exec deployment-kanto build-vehicleapp
velocitas exec deployment-kanto deploy-vehicleapp

You can now test the application by interacting with it through the local MQTT broker. To send and receive MQTT messages, use the VSMQTT plugin with a cloud icon in the left side of the VSCode instance connected to the DevContainer. We do not get into details for the topics and messages here since we will run the application in Eclipse Leda again later in this guide.

Once you finish the application testing and development, you can shutdown the runtime with:

velocitas exec runtime-kanto down

Eclipse Velocitas provides Tasks to abstract the mentioned calls of the velocitas CLI. As an alternative you can thus press F1 -> Run Task and scroll for the respective task like Kanto runtime Up.

Commit and Release Application

When you are confident about the application, you want to be able to distribute it. Here, pre-configured workflows from Eclipse Velocitas for GitHub Actions are helpful.

You now commit your changes and push them to your copy of the Eclipse Velocitas template repository in GitHub. Before you create the commit, we recommend running the pre-commit task, which performs similar checks and linting as the CI workflow from Eclipse Velocitas. You can trigger the pre-commit as a task in VSCode:

  1. Press F1
  2. Write Tasks: Run Task and press enter
  3. Write Pre Commit Action and press enter

The pre commit action should start in a terminal inside VSCode.

You can check the available tasks configured by Eclipse Velocitas in .vscode/tasks.json.

If the pre-commit was successful, you may push your changes and open the repository in the browser. In the meantime, the push should trigger the CI workflow and the Build multiarch image workflow, which you can track in the Actions tab.

If you set your copy of the template repository to private, the CI workflow may fail due to missing permissions to write container images to GitHub packages. You can grant more Workflow permissions in the Settings tab of the repository under Actions-> General.

To deploy your application to a target and if the two workflows have finished successfully, you can perform a release in GitHub. The subsequent release workflow will make the application available as a built container in the container registry of GitHub (Code -> Packages). To do the release in GitHub, go to the Code tab and click Releases on the right side of the page. Then you can Draft a new release, create a new tag and title for the release, and click Publish Release, which triggers the Release workflow.

The next step is to deploy the seat adjuster in Eclipse Leda

1.3 - Deploy Seat Adjuster

We now want to deploy the application to a target device. You may follow the remainder of this guide on a separate device like a RaspberryPi, but you can emulate such a device on your development machine too. Either way, we use Eclipse Leda in version 0.1.0-M2 as the target system, which is a Linux-based distribution with pre-installed SDV components like the KUKSA Databroker and Eclipse Kanto for container management. For more details on how to download and run Eclipse Leda, follow the respective guides:

We recommend to get started with the QEMU setup. In any case, you now need to configure Eclipse Kanto to execute the application. For this, it helps to get an overview of which containers are currently running in Eclipse Kanto. You can get this either through the command:

kantui

or

kanto-cm list

From this list, ensure that at least the KUKSA Databroker runs, which should be the case since it is are pre-configured with the Eclipse Leda release.

In Eclipse Kanto, you can manage a container with the command line application kanto-cm or container manifest files describing a desired container execution. The advantage of using the container manifests is that the configuration is persisted across a reboot of the system and is easier to use to describe a desired software state for the overall vehicle.

Eclipse Leda has the kanto-auto-deployer systemd service, which applies any changes to the manifests in /data/var/containers/manifests to Eclipse Kanto. Thus, the typical way to add or adapt containers is to modify the corresponding container manifest.

Disable other containers

The release 0.1.0-M2 of Eclipse Leda comes with a number of pre-configured and automatically executed containers. One of these containers is the feedercan that feeds changing values from a recording for signals such as Vehicle.Speed to the KUKSA Databroker. These values interfere with the seat adjuster application, which only moves the seat if the vehicle speed is zero. Another interfering container is the seatservice-example which reacts to changes in the signal Vehicle.Cabin.Seat.Row1.Pos1.Position and which we replace later with the mock service.

Therefore, we need to stop the feedercan and the seatservice-example container. This is possible in kantui by selecting the respective entry and pressing R. In addition, you need to remove the corresponding container manifests in /data/var/containers/manifests to avoid that the Eclipse Kanto auto-deployer re-deploys these containers. Another approach is to change the ending of the not-needed manifests to something other than .json.

If the feedercan container still runs, the seat adjuster application app will later respond with the following error message:

seatadjuster/setPosition/response
 {"requestId": "12345", "result": {"status": 1, "message": "Not allowed to move seat because vehicle speed is 9.0 and not 0"}}

Since they consume resources and are not needed for the seat adjustment, you may remove the containers and manifests for cloudconnector, hvacservice-example, sua, or vum as well.

Starting of container

Use kanto-cm

kanto-cm create \
    --name seatadjuster-app \
    --e="SDV_SEATSERVICE_ADDRESS=grpc://seatservice-example:50051" \
    --e="SDV_MQTT_ADDRESS=mqtt://mosquitto:1883" \
    --e="SDV_VEHICLEDATABROKER_ADDRESS=grpc://databroker:55555" \
    --e="SDV_MIDDLEWARE_TYPE=native" \
    --hosts="databroker:container_databroker-host, mosquitto:host_ip, seatservice-example:container_seatservice-example-host" \
    ghcr.io/<YOUR_ORG>/seat-adjuster-app:latest

kanto-cm start --name seatadjuster-app
kanto-cm logs --name seatadjuster-app

Add manifest for seat adjuster

As an alternative to using kanto-cm, you can add a container manifest to the directoy watched by the kanto-auto-deployer (data/var/containers/manifests).

To add the container manifest, create a new file inside this folder.

touch seat-adjuster.json
nano seat-adjuster.json

and copy the manifest from below. You can save the file with strg+s and close the window with strg+q. You can create the file on the development machine and copy it via scp too:

scp -P 2222 myapp.json root@localhost:/data/var/containers/manifests/

The example deployment descriptor below is available in meta-leda-components too. An interesting aspect of the snippet is the config.env section at the bottom of the container manifest. There, we define a number of environment variables for the container which configures the Eclipse Velocitas SDK to use the native middleware and where to find the MQTT-broker and the KUKSA Databroker to use. We did the same in kanto-cm call behind the parameter --e=.

More details on the general deployment approach can be found in Leda Vehicle Applications

If the GitHub packages in which you stored the container image are private, Eclipse Kanto needs a valid access token to download the container image. You can create a personal access token in the Developer Settings of your GitHub account. Select Personal access token -> Tokens (classic) and generate a new token that at least has the read:packages permission. Copy the generated token to a secure location or to Eclipse Kanto because GitHub will not show it again. You can now configure Eclipse Kanto in Eclipse Leda to use the token by executing: sdv-kanto-ctl add-registry -h <registryhostname> -u <your_username> -p <your_password>. In the case of GitHub, the registryhostname is ghcr.io , the username is your GitHub handle, and the password is the generated token. See Container Registries for more details.

To make sure that Eclipse Kanto detects the changes in the manifests folder, you can restart the respective system services:

systemctl restart kanto-auto-deployer

Mock Service

As explained in the description of the code, the seat adjuster application sets the target value for the seat positions in the KUSKSA Datbroker and waits for the current position to update.

For this to function, there needs to be a component that reacts to this change by moving the seat and updating the current value accordingly. Because we cannot assume that you have an actual ECU availabe for running this guide, we mock the vehicle behavior with the vehicle mock service from the Eclipse Kuksa project.

The mock service allows the definition of custom interaction sequences with the KUKSA Databroker. For instance, one can react to changes to specific signals or update signals with a time trigger. You can define the sequences in a Python file like the example mock.py below. The snippet shows how to use a change to the target value for the seat position signal as a trigger to update the current value to the target value step-wise over a duration of 10 seconds.

For the deployment, you create another container manifest in /data/var/containers/manifest with the content from below. The container manifest also mounts a custom mock.py into the container to replace the configuration of the default mock.py. With the container manifest below, Eclipse Kanto instead mounts from /data/var/mock/mock.py. Therefore, you need to create this directory and the file with the content from below.

You may now check with kantui or kanto-cm list whether all components (databroker, seatadjuster-app, and mock-service) are running well. The next step is to interact with the seat adjuster.

seat-application.json

This is the Eclipse Kanto container manifest for the seat adjuster application.

{
    "container_id": "seatadjuster-app",
    "container_name": "seatadjuster-app",
    "image": {
        "name": "ghcr.io/<identifier-for-container>:<tag-for-container>"
    },
    "host_config": {
        "devices": [],
        "network_mode": "bridge",
        "privileged": false,
        "restart_policy": {
            "maximum_retry_count": 0,
            "retry_timeout": 0,
            "type": "unless-stopped"
        },
        "runtime": "io.containerd.runc.v2",
        "extra_hosts": [        
                "mosquitto:host_ip",
                "databroker:container_databroker-host",
                "seatservice-example:container_seatservice-example-host"
        ],
        "port_mappings": [
            {
              "protocol": "tcp",
              "container_port": 30151,
              "host_ip": "localhost",
              "host_port": 50151,
              "host_port_end": 50151
            }
        ],
        "log_config": {
            "driver_config": {
                "type": "json-file",
                "max_files": 2,
                "max_size": "1M",
                "root_dir": ""
            },
            "mode_config": {
                "mode": "blocking",
                "max_buffer_size": ""
            }
        },
        "resources": null
    },
    "config": {
        "env": [
           "SDV_SEATSERVICE_ADDRESS=grpc://seatservice-example:50051",
           "SDV_VEHICLEDATABROKER_ADDRESS=grpc://databroker:55555",
           "SDV_MQTT_ADDRESS=mqtt://mosquitto:1883",
           "SDV_MIDDLEWARE_TYPE=native",
           "RUST_LOG=info",
           "vehicle_data_broker=info"
        ],
        "cmd": []
    }
}

mock.py

This is the example mock.py for mocking a seat provider:

from lib.animator import RepeatMode
from lib.dsl import (
    create_animation_action,
    create_behavior,
    create_event_trigger,
    create_set_action,
    get_datapoint_value,
    mock_datapoint,
)

from lib.trigger import ClockTrigger, EventType

mock_datapoint(
    path="Vehicle.Cabin.Seat.Row1.Pos1.Position",
    initial_value=0,
    behaviors=[
        create_behavior(
            trigger=create_event_trigger(EventType.ACTUATOR_TARGET),
            action=create_animation_action(
                duration=10.0,
                values=["$self", "$event.value"],
            ),
        )
    ],
)

mockservice.json

The container manifest for the mockservice may look like the following snippet. Note, that the files referenced in the source of the mount_points needs to be present in the file system of your Eclipse Leda instance.

{
    "container_id": "mockservice",
    "container_name": "mockservice",
    "image": {
        "name": "ghcr.io/eclipse/kuksa.val.services/mock_service:latest"
    },
    "mount_points": [
        {
            "source": "/data/var/mock/mock.py",
            "destination": "/mock.py",
            "propagation_mode": "rprivate"
        }
    ],
    "host_config": {
        "network_mode": "bridge",
        "privileged": false,
        "restart_policy": {
            "maximum_retry_count": 0,
            "retry_timeout": 0,
            "type": "unless-stopped"
        },
        "runtime": "io.containerd.runc.v2",
        "extra_hosts": [
            "databroker:container_databroker-host"
        ],
        "log_config": {
            "driver_config": {
                "type": "json-file",
                "max_files": 2,
                "max_size": "1M",
                "root_dir": ""
            },
            "mode_config": {
                "mode": "blocking",
                "max_buffer_size": ""
            }
        }
    },
    "config": {
        "env": [
           "VDB_ADDRESS=databroker:55555"
        ]
    }
}

1.4 - Interact with Seat Adjuster

We interact with the seat application through MQTT messages to emulate the behavior of an offboard application. To see the responses from the seat adjuster app, subscribe to the MQTT topic `seatadjuster/#``.

mosquitto_sub -t 'seatadjuster/#' -v

To initiate the moving of the seat, publish an MQTT message for the seat adjuster application to set the position to 1000 (which is the equivalent of 100%):

mosquitto_pub -t seatadjuster/setPosition/request -m '{"position": 1000, "requestId": "12345"}'

You may need to open a second terminal session to send the message. In the QEMU setup, you can do this through an SSH connection from the host machine by running ssh -p 2222 root@localhost.

After publishing the setPosition-message, the expected output for the seatadjuster/# subscription should look like this:

seatadjuster/setPosition/request {"position": 1000, "requestId": "12345"}
seatadjuster/setPosition/response {"requestId": "12345", "result": {"status": 0, "message": "Set Seat position to: 1000"}}
seatadjuster/currentPosition {"position": 00}
seatadjuster/currentPosition {"position": 10}
seatadjuster/currentPosition {"position": 20}
seatadjuster/currentPosition {"position": 30}
seatadjuster/currentPosition {"position": 40}
seatadjuster/currentPosition {"position": 50}
...

If you encounter an issue or want to look deeper into the state of the container, you can check the logs by executing:

kanto-cm --name <container-name> logs

Run Kuksa Client

Besides trusting the log information and the MQTT messages that the seat position in the KUKSA Databroker has changed you can read the values directly from the KUKSA Databroker with the KUKSA Client. Through the sdv-ctr-exec it becomes possible to interact with the CLI of the Eclipse KUKSA Client:

kanto-cm create --i --t --network=host --name=kuksa-client ghcr.io/eclipse/kuksa.val/kuksa-client:master
kanto-cm start --name=kuksa-client
sdv-ctr-exec -n kuksa-client /kuksa-client/bin/kuksa-client --port 30555 --protocol grpc --insecure

Congratulations, you have reached the end of the seat adjuster guide. Based on the used code, you can now continue to realize other applications by using other signals. You may need to adapt your mock.py accordingly. In addition, you can connect the seat service to a CAN-environment.

References

1.5 - CAN Setup for Seat Adjuster

Within the seat adjuster guide, we so far mocked the behavior of the actual vehicle. In the following, we give an overview of how to create a virtual CAN-bus and interact with it to send CAN-frames for moving a seat.

Instead of the mock service you use the seat service as a provider, which is another pre-installed container acting as the connection between the KUKSA Databroker and the underlying hardware. More specifically, it subscribes to the target value of the Vehicle.Cabin.Seat.Row1.Pos1.Position signal, can send CAN-frames, and updates the current value of the signal in small steps until it is equal to the target value. For more details, visit the Kuksa.val.services repository, which hosts the code for the seat service.

With a new installation of Eclipse Leda, the seat adjuster comes by default. If you performed the steps from the deploy the seat adjuster in Eclipse Leda guide, you need to re-add the seatadjuster container manifest and remove the mockservice. More details on how to do this are available in that guide.

The setup then looks as follows:

flowchart TB client[Client] anotherClient[Another Client] seatadjuster[Seat Adjuster] databroker[(KUKSA
Databroker)] mqttRequest[[MQTT topic
seatadjuster/setPosition/request]] %% mqttResponse[[MQTT topic
seatadjuster/setPosition/response]] %% mqttCurrent[[MQTT topic
seatadjuster/currentPosition]] seatservice[Seat Service] canbus[CAN-Bus and Seat ECU] client -- "JSON Request: position, requestId" --> mqttRequest anotherClient -.-> mqttRequest mqttRequest --> seatadjuster seatadjuster -- "Set Target
Vehicle.Cabin.Seat.Row1.Pos1.Position
gRPC" --> databroker databroker <-- "Set Current
Vehicle.Cabin.Seat.Row1.Pos1.Position

Notify subscriber of changed target
Vehicle.Cabin.Seat.Row1.Pos1.Position" --> seatservice seatservice -- "CAN Frame: SECU1_CMD1
CAN-ID 0x705" --> canbus

Virtual CAN-Bus

To set upa virtual CAN-Bus

  1. You will have to generate initial CAN frames that will emulate the car ECU responding to the service:

    $ cangen -v can0 -L 8 -I 712 -D r -n 5
    can0  712#4F.BF.B0.6B.5F.2D.54.09
    can0  712#13.2E.98.7E.77.11.99.5B
    can0  712#15.70.87.07.73.24.3A.7A
    can0  712#99.7F.F5.3F.FB.99.00.04
    can0  712#FE.1C.D5.55.22.86.3A.1F
    
  2. You can now start tracing CAN frames written to the bus with candump can0

  3. From now on when a request to change the seat position is issued you will be able to see the corresponding CAN frames in the trace.

Note: On QEMU you can tunnel the host CAN bus to the guest: Tunneling a CAN Interface from the Host.

Hardware CAN-Bus

The default configuration of the Seat Service is using simulated VCAN. If you want to switch to a physical CAN-Bus interface, the container needs to have access to the CAN-Bus hardware.

Such a CAN-Bus device might be a Raspberry Pi setup with an MCP251x-based CAN-Hat extension or a QEMU image with an emulated kvaser_pci device (enabled on the Leda QEMU Quickstart images by default).

This setup would require some adjustments to the container manifest in order for the container to have access to the physical CAN-Bus.

  1. Make Seat Service container privileged and run on the host network interface:

    "host_config": {
    ...
    "network_mode": "host",
    "privileged": true,
    ...
    }
    
  2. Remove all port mappings and extra hosts (set "extra_hosts": [] and "port_mappings": []) for the container as it’s now running in host-networking mode (host_ip variable no longer available) and all ports are directly exposed.

  3. Set the address to the databroker to localhost:30555:

        "config": {
        "env": [
           ...
            "BROKER_ADDR=127.0.0.1:30555",
           ...
        ],
        ...
        }
    
  4. Reconfigure the seat controller application to use the physical CAN interface,please see Eclipse Kuksa.VAL seat_controller/README.md for details:

    SC_CAN=can0
    CAN=can0
    

    All the necessary changes combined for clarity as a single diff can be found below:

    --- ../meta-leda-fork/meta-leda-components/recipes-sdv/eclipse-leda/kanto-containers/example_dev/seatservice.json	2023-03-06 11:32:00.771754434 +0200
    +++ seatservice-new.json	2023-03-06 11:37:12.967182044 +0200
    @@ -14,26 +14,16 @@
        "hooks": [],
        "host_config": {
            "devices": [],
    -        "network_mode": "bridge",
    -        "privileged": false,
    +        "network_mode": "host",
    +        "privileged": true,
            "restart_policy": {
                "maximum_retry_count": 0,
                "retry_timeout": 0,
                "type": "unless-stopped"
            },
            "runtime": "io.containerd.runc.v2",
    -        "extra_hosts": [
    -            "databroker-host:host_ip"
    -        ],
    -        "port_mappings": [
    -            {
    -              "protocol": "tcp",
    -              "container_port": 50051,
    -              "host_ip": "localhost",
    -              "host_port": 30051,
    -              "host_port_end": 30051
    -            }
    -        ],
    +        "extra_hosts": [],
    +        "port_mappings": [],
            "log_config": {
                "driver_config": {
                    "type": "json-file",
    @@ -58,9 +48,11 @@
        },
        "config": {
            "env": [
    -           "BROKER_ADDR=databroker-host:30555",
    -           "RUST_LOG=info",
    -           "vehicle_data_broker=info"
    +            "CAN=can0",
    +            "SC_CAN=can0",
    +            "BROKER_ADDR=127.0.0.1:30555",
    +            "RUST_LOG=info",
    +            "vehicle_data_broker=info"
            ],
            "cmd": []
        },
    

Safety Considerations

Attention: Safety considerations are not in scope for this example tutorial. This example is for demonstrating the general approach. Actual safety requirements must be handled within the Seat ECU as the lowest level component to guard against non-safe use of the seat motors. Non-ASIL domains are not certified for safety requirements. Please pay attention when following the physical CAN tutorial and attaching physical actuators to not harm anybody by accidental movements of seat motors or any other actuator.

However, the Seat Adjuster example application contains a rudimentary “Safe State” condition check: it will only allow to move the seat when the vehicle is not moving.

The condition is using VSS path notation: Vehicle.Speed == 0 (see main.py#L82 in v0.9.0)

Note: The Kuksa.VAL CAN Feeder, which is deployed by default on Eclipse Leda is constantly updating the Vehicle.Speed You need to disable the feedercan container (see step 7 of Getting started), otherwise the Seat Adjuster application will decline the request and not move the seat.

2 - Dog Mode

The Dog Mode use case has been derived from the Eclipse Velocitas Dog Mode example and the Eclipse Kuksa.VAL HVAC Service example.

Description

In the Dog Mode use case, an existing vehicle service (such as HVAC for Heating, Ventilation and Air Conditioning) is used for another use case. When a driver enables the dog mode, the vehicle will keep the climate in a status to accomodates the pet while the driver is gone for a few minutes.

The focus on this example is to show how an additional application can reuse existing functionality for new use cases. The vehicle service being used does not need to be adapted for the new use case. The new use case is deployed as an additional functionality, separated from the rest of the existing system.

Architecture Overview

Leda Dog Mode Use Case

  • Cloud or mobile trigger: not part of the Leda image, but can be simulated by issueing MQTT messages
  • Eclipse Velocitas - Vehicle Application: Dog Mode (to be deployed by user as part of the Velocitas tutorial)
  • Eclipse Kuksa.VAL - Example Service: HVAC Service (pre-installed)
  • Eclipse Kuksa.VAL - Data Broker (pre-installed)

Getting started

  1. Follow the Velocitas tutorial: build and deploy your clone of the dog mode example

  2. Download and run the Leda quickstart image

  3. Deploy the application to the container runtime, either manually by using kanto-cm create or by providing a deployment descriptor in /var/containers/manifests. An example deployment descriptor can be found in meta-leda-components. Details on the deployment can be found in Leda Vehicle Applications

  4. Ensure the databroker and the service containers are running and you know how to check their log files

  5. Monitor vehicle data: mosquitto_sub -t dogmode/display

  6. Enable dog mode by using the databroker-cli

     connect
     set Vehicle.Cabin.DogMode 1
    

3 - VSS & Kuksa.VAL

To generically interact with the Vehicle Signals which are managed in the Eclipse Kuksa.VAL Databroker, you can either use the natively installed databroker-cli command line tool, or use the CLI tool from an updated and released containerized image.

Kuksa Databroker CLI

To install a new version of the Kuksa Databroker CLI container, follow these steps:

  1. Create the container in Kanto:

     kanto-cm create --i --t --network=host --name=databroker-cli ghcr.io/eclipse/kuksa.val/databroker-cli:master
    
  2. Start the command line tool within a container in interactive mode:

     kanto-cm start --a --i --name databroker-cli
    
  3. In the databroker shell, connect to the databroker via the URL http://127.0.0.1:30555/:

     connect http://127.0.0.1:30555/
    

Kuksa Databroker CLI Container

Kuksa Client

To install a new version of the Kuksa Client container, follow these steps:

  1. Pull the container image

     ctr --namespace kanto-cm image pull ghcr.io/eclipse/kuksa.val/kuksa-client:master
    
  2. Create the container in Kanto Namespace:

     ctr --namespace kanto-cm container create --net-host --tty ghcr.io/eclipse/kuksa.val/kuksa-client:master kuksa-client
    
  3. Start the container in detached mode:

     ctr --namespace kanto-cm tasks start --detach kuksa-client
    
  4. Start the command line tool with additional command line options:

     ctr --namespace kanto-cm tasks exec --tty --exec-id sometask kuksa-client /kuksa-client/bin/kuksa-client --port 30555 --protocol grpc --insecure
    

3.1 - Custom Vehicle Signals

The Vehicle Signal Specification has a lot of standard Vehicle Signals already defined.

However, if you want to add additional custom signals or mappings from CAN-Bus for the Kuksa.VAL dbc2val feeder, the VSS data model needs to be customized and regenerated.

The following steps can be performed on a development machine to regenerate VSS data model with custom a vspec mapping.

  1. Concepts
  2. Setup workspace
  3. Defining the VSS Overlay
  4. Regenerate VSpec JSON
  5. Generate a candump
  6. Databroker and dbc2val containers
  7. Testing with data

Concepts

VSS Overlay

The VSS Overlay concept allows to override the standard signals in the specification with custom signals. It defines metadata attributes like signal name, datatype, description etc.

In addition, the vspec file allows to add custom metadata, such as the dbc node in the following example:

Vehicle.Speed:
  type: sensor
  datatype: float
  dbc:
    message: Kombi_01
    signal: KBI_angez_Geschw
    interval_ms: 1000

See Eclipse Kuksa documentation about dbc2val mapping for details.

There are also ways to automatically transform the CAN-Bus signal values into VSS values:

  • Mapping of literal values, such as enums and booleans
  • Mathematical transformations
  • Full and partial transformations

Setup workspace

Required tools

  • Git
  • Docker
  • Python 3
  • can-utils
  • a YAML/JSON text editor

Depending on your host OS, you may need to install the packages first:

sudo apt-get update
sudo apt-get install can-utils git docker python3

Workspace structure

You need the following files in your workspace folder:

  • ./custom.dbc - The vehicle-specific DBC file. You can use the Tesla Model 3 example or check out the opendbc project.
  • ./custom.vspec - The VSS overlay from the previous step

Defining the VSS Overlay

The definition of an VSS overlay is a manual process. It requires research, browsing the existing VSS catalog and deep knowledge of the vehicle signals. Especially knowing the semantic of a signal and how to map it between DBC and VSS is a complex task.

Note: The Leda team is not aware of any advanced tooling to support the automation of this task.

  1. Create a new file custom.vspec. Use YAML format
  2. For each identified signal:
    1. Look up if there already is a corresponding signal in VSS. You may use the Digital.Auto Playground or the spec directly.
    2. Add a new top-level entry to the custom.vspec file. The key is the full VSS path name in dot notation.
    3. Add the corresponding dbc node
    4. Add the signal attribute and enter the name of the CAN-Bus signal name in the DBC
    5. Add additional attributes such as interval_ms, message or transform.

      Note: The attributes signal, interval_ms and transform are evaluated by dbc2val. The attribute message is only evaluated by the below demo script and is optional.

Example:

# CAN Message: Kombi_01
Vehicle.Speed:
  type: sensor
  datatype: float
  dbc:
    message: Kombi_01
    signal: KBI_angez_Geschw
    interval_ms: 1000

# CAN Message: FS_Position_01
Vehicle.Cabin.Seat.Row1.Pos1.Position:
  type: sensor
  datatype: float
  dbc:
    message: FS_Position_01
    signal: FSS_Laenge_Pos
    interval_ms: 1000

These two extensions will be added to the combined VSS tree:

VSS Overlay with custom CAN-Bus metadata

Regenerating VSpec

The script vspec2json.py from COVESA VSS Tools can then be used to merge the overlay file with the standard specification.

For Leda devices, you can use kanto-cm to run the tool as a container:

# Copy custom.dbc and custom.vspec to /home/root
# Run in /home/root
kanto-cm create --name vspec2json --rp no --mp="/home/root:/data" ghcr.io/eclipse-leda/leda-vss-vspec2json:main
kanto-cm start --a --i --name vspec2json

For developer workstations, there is a convenient pre-built Docker container:

docker run --name vspec2json --rm -v `pwd`:/data ghcr.io/eclipse-leda/leda-vss-vspec2json:main

Or, if you want to run vspec2json from the sources, clone the repository and invoke it like this:

git clone \
    --recurse-submodules \
    --branch v3.1 \
    --single-branch \
    --depth 1 \
    https://github.com/COVESA/vehicle_signal_specification

python3 vehicle_signal_specification/vss-tools/vspec2json.py \
    -e dbc \
    -o custom.vspec \
    --uuid \
    --json-pretty \
    --json-all-extended-attributes \
    ../vehicle_signal_specification/spec/VehicleSignalSpecification.vspec \
    custom_vss_dbc.json

With the resulting file custom_vss_dbc.json and the corresponding DBC file, the dbc2val feeder can relay the signals to the databroker. dbc2val can either read from a CAN-Dump file (generated by candump from can-utils) in an endless loop, or can listen on actual virtual or physical CAN interfaces.

The output should look like this:

$ ls
custom.dbc  custom.vspec
$ docker run --name vspec2json --rm -v `pwd`:/data ghcr.io/eclipse-leda/leda-vss-vspec2json:main
INFO     Output to json format
INFO     Known extended attributes: dbc
INFO     Added 54 units from /vss/spec/units.yaml
INFO     Loading vspec from /vss/spec/VehicleSignalSpecification.vspec...
INFO     Applying VSS overlay from /data/custom.vspec...
INFO     Autocreating implicit branch Vehicle
INFO     Autocreating implicit branch Cabin
INFO     Autocreating implicit branch Seat
INFO     Autocreating implicit branch Row1
INFO     Autocreating implicit branch Pos1
INFO     Calling exporter...
INFO     Generating JSON output...
INFO     Serializing pretty JSON...
INFO     All done.
$ ls
custom.dbc  custom.vspec  custom_vss_dbc.json

Databroker Container

Ensure the databroker container is up and running:

On developer workstation:

docker run --name databroker --detach --rm -p 55555:55555/tcp ghcr.io/eclipse/kuksa.val/databroker:0.3.1

On Leda device (if not already running)

kanto-cm start --name databroker

Testing with data

To see actual data, we need to use the candump or another CAN source and feed that data into the databroker. After that is running, we can use the databroker-cli tool to subscribe to the example VSS signals.

Generate a candump

To generate a candump for testing purposes, start the candump tool and listen for some packets.

In the following example, the first 20 CAN frames are being recorded and the command will exit.

candump -L -n 20 vcan0 > candump.log

If you do not have an external CAN source, you may want to simulate CAN frames. There is a Python library for parsing DBC files, which also let’s you encode and send the actual CAN messages.

An example script could look like this:

Note: Due to GPL-3 licensing, the Leda quickstart image does not contain some Python standard libraries. This script will not run on a Leda quickstart image.

import random
import time
from pprint import pprint

import yaml
import cantools
import can

can_bus = can.interface.Bus('vcan0', bustype='socketcan')
db = cantools.database.load_file('custom.dbc')
with open("custom.vspec", "r") as stream:
    try:
        vspec = yaml.safe_load(stream)

        for key in vspec:
            message_name=vspec[key]['dbc']['message']
            signal_name=vspec[key]['dbc']['signal']
            example_message = db.get_message_by_name(message_name)

            # Initialize an empty message
            data = {}
            for signal in example_message.signals:
                data[signal.name] = False

            pprint("Message: %s" % example_message)

            # Send 10 frames with random data
            for x in range(10):
                data[signal_name] = random.uniform(0,100)
                encoded_data = example_message.encode(data)
                message = can.Message(arbitration_id=example_message.frame_id, data=encoded_data)
                pprint("CAN Frame: %s" % message)
                can_bus.send(message)
                # Wait up to 1 second
                time.sleep(random.random())

    except yaml.YAMLError as exc:
        print(exc)

Example output:

"Message: message('Kombi_01', 0x30b, False, 8, {None: 'MQB'})"
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 b9 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 16 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 ca 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 ad 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 b0 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 79 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 1b 01')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 19 01')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 0a 00')
('CAN Frame: Timestamp:        0.000000    ID: 0000030b    X Rx                '
 'DL:  8    00 00 00 00 00 00 b0 00')
"Message: message('FS_Position_01', 0x6be, False, 8, None)"
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    59 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    31 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    29 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    07 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    40 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    55 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    54 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    07 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    62 00 00 00 00 00 00 00')
('CAN Frame: Timestamp:        0.000000    ID: 000006be    X Rx                '
 'DL:  8    59 00 00 00 00 00 00 00')

dbc2val on workstation

Get the latest sources:

git clone https://github.com/eclipse/kuksa.val.feeders

Looping a CAN dump recording:

kuksa.val.feeders/dbc2val/dbcfeeder.py \
    --dumpfile candump.log \
    --canport vcan0 \
    --server-type kuksa_databroker \
    --dbcfile custom.dbc \
    --mapping custom_vss_dbc.json

Listening on CAN interface:

# CAN must be set up up front, e.g.
# modprobe vcan
# ip link add dev vcan0 type vcan
# ip link set vcan0 up

kuksa.val.feeders/dbc2val/dbcfeeder.py \
    --canport vcan0 \
    --use-socketcan \
    --server-type kuksa_databroker \
    --dbcfile /custom/custom.dbc \
    --mapping /custom/custom_vss_dbc.json

dbc2val on Leda device

To run the dbc2val feeder on Leda, you can use kanto-cm (instead of docker) to create the respective containers.

Note: Due to Issue #73, the container ghcr.io/eclipse/kuksa.val.feeders/dbc2val:v0.1.1 cannot be used with SocketCAN. Please use a later release or build the container from the sources after fixing the bug. Alternatively, use the candump file instead of SocketCAN.

  1. Remove any existing dbc2val container:

    kanto-cm remove --force --name dbc2val
    
  2. Create the new container in Kanto Container Management:

    kanto-cm create \
        --name dbc2val \
        --mp="/home/root:/data" \
        --e=VDB_ADDRESS="databroker:30555" \
        --e=USECASE="databroker" \
        --network=host \
        --privileged \
        ghcr.io/eclipse/kuksa.val.feeders/dbc2val:v0.1.1 \
        /dist/dbcfeeder-exe \
        --canport vcan0 \
        --use-socketcan \
        --usecase databroker \
        --dbcfile /data/custom.dbc \
        --mapping /data/custom_vss_dbc.json
    

    Note: The host networking and privileged is used to allow the container access to the CAN sockets.

  3. Start the container:

    kanto-cm start --a --i --name dbc2val
    
  4. Check the logs to verify container is running:

    kanto-cm logs --name dbc2val
    

Querying via CLI

To be able to see the changes, you may want to use the Kuksa Databroker CLI:

docker run --rm -it --network host ghcr.io/eclipse/kuksa.val/databroker-cli:0.3.1

Now, depending on the way how the dbc2val feeder has been started (candump vs. socketcan), you will either already see data from the candump loop.

Eclipse Kuksa.VAL Databroker CLI

4 - CarSim

The CarSim example provides basic Vehicle Signals based on a physical model simulation and feeds them to the databroker.

Eclipse Leda CarSim Kinetics

Architecture Overview

Eclipse Leda CarSim DriverSim Architecture Overview

Description

CarSim is a simple physical model implementation, based on the kinematic bicycle model. It can be used as a mock implementation in development, testing and simulation scenarios.

It takes three external inputs:

  • accelerator position
  • brake position
  • steering wheel angle

It propagates the physical model in time with a pre-defined step interval (10ms), recalculates the acceleration components, speed and position, and finally publishes the numeric values as standard VSS datapoints to the databroker.

To simulate the input from a human driver, the optional DriverSim component can be run in parallel. DriverSim toggles between two different types of driver style in a pre-defined time interval:

  • Good driver - slowly accelerates and moves the steering angle accordingly and then slowly brakes.
  • Bad driver - accelerates hard (full throttle), moves the steering wheel randomly in both directions, brakes hard.

Background

Decoupled Design

CarSim and DriverSim only communicate through the databroker by using standardized Vehicle Signals.

Each component can be replaced individually at runtime, e.g. by more advanced or real implementations. There is no need to adapt, recompile or redeploy any of the other applications.

This is important for decoupling and standardizing vehicle signals, to enable future vehicle application platforms to evolve. Loose coupling and standardizing of vehicle signal interfaces enable thirdparty applications to use signals in higher-level applications or value-add services.

Both implementors (imagine separate organizations), can agree to use the Vehicle Signal Specification to decouple the development lifecycle for vehicle applications and vehicle services. This decoupling approach reduces the time and effort for proprietary design specifications processes tremendously.

Vehicle Signals

The CarSim application reads the following input signals from the databroker:

Input

“Human” driver input Python variable VSS subscription path VSS datatype
Brake pedal position DP_BRAKE_POS Vehicle.Chassis.Brake.PedalPosition uint8
Accelerator pedal position DP_ACCELR_POS Vehicle.Chassis.Accelerator.PedalPosition uint8
Steering wheel angle DP_STEER_ANGLE Vehicle.Chassis.SteeringWheel.Angle int16

Note: You can either provide these signals externally on your own, or use DriverSim to simulate them.

Output

The CarSim application feeds the following output signals to the databroker:

Python variable VSS feeding path VSS datatype
DP_SPEED Vehicle.Speed float
DP_ACCEL_LAT Vehicle.Acceleration.Lateral float
DP_ACCEL_LONG Vehicle.Acceleration.Longitudinal float
DP_ACCEL_VERT Vehicle.Acceleration.Vertical float

Getting started

  1. Start up Leda and wait for the runtime to be available. For other deployment options, see Getting Started

    docker run -it ghcr.io/eclipse-leda/leda-distro/leda-quickstart-x86
    
  2. Deploy the CarSim container:

    kanto-cm create --name carsim --e=DATABROKER_ADDRESS=databroker:55555 --hosts="databroker:container_databroker-host" ghcr.io/eclipse-leda/leda-example-applications/leda-example-carsim:v0.0.1
    kanto-cm start --name carsim
    
  3. Deploy DriverSim container:

    kanto-cm create --name driversim --e=DATABROKER_ADDRESS=databroker:55555 --hosts="databroker:container_databroker-host" ghcr.io/eclipse-leda/leda-example-applications/leda-example-driversim:v0.0.1
    kanto-cm start --name driversim
    
  4. Use kantui or kanto-cm logs --name <carsim|driversim> to view log files:

    Eclipse Leda KantUI CarSim Logs

    Eclipse Leda KantUI DriverSim Logs

  5. Use the databroker-cli to subscribe to the following VSS signals.

    Note: Separate multiple signals by commas. Use <TAB> to auto-complete signal names. See QUERY.md for the query syntax.

    $ databroker-cli
    connect localhost:30555
    subscribe
        SELECT
         Vehicle.Speed,
         Vehicle.Acceleration.Vertical,
         Vehicle.Acceleration.Longitudinal,
         Vehicle.Acceleration.Lateral
        WHERE
         Vehicle.Speed > 50
    

    Eclipse Leda CarSim Databroker-CLI

  6. Happy hacking!

    Need ideas? How about implementing your own driver simulation and integrate with existing open source simulation technologies from Autonomous Driving?

References

5 - Velocitas VApps

For local deployment without a cloud backend, deployment specifications for Eclipse Velocitas Vehicle Apps may be put directly onto the device.

  • Create a Kanto deployment specification for your vehicle app. You may copy one of the existing JSON files in /data/var/containers/manifests as a template
  • Copy the specification file to the device, e.g. scp -P 2222 myapp.json root@localhost:/data/var/containers/manifests/
  • Apply the specification: systemctl restart kanto-auto-deployer

To update an existing container when the configuration has changed, delete the container and restart kanto-auto-deployer:

kanto-cm remove -n myapp-example
# Edit /data/var/containers/manifests/myapp.json
kanto-auto-deployer

Configuration of Velocitas Middleware Type

The following Velocitas environment variables need to be set to configure the proper middleware type and hostnames of services:

SDV_MQTT_ADDRESS=mosquitto:1883
SDV_VEHICLEDATABROKER_ADDRESS=databroker:55555
SDV_MIDDLEWARE_TYPE=native

Example Deployment Specification

Attention: The current implementation requires all fields to be present in the JSON, even if the values are not set or used. Do not remove any fields, as it may break the functionality.

{
    "id": "",
    "name": "myapp-example",
    "image": {
        "name": "ghcr.io/my-org/my-repo/my-app:latest",
        "decrypt_config": null
    },
    "host_name": "",
    "domain_name": "",
    "resolv_conf_path": "",
    "hosts_path": "",
    "hostname_path": "",
    "mounts": [],
    "hooks": [],
    "host_config": {
        "devices": [],
        "network_mode": "bridge",
        "privileged": false,
        "restart_policy": {
            "maximum_retry_count": 0,
            "retry_timeout": 0,
            "type": "unless-stopped"
        },
        "runtime": "io.containerd.runc.v2",
        "extra_hosts": [],
        "port_mappings": [
            {
              "protocol": "tcp",
              "container_port": 30151,
              "host_ip": "localhost",
              "host_port": 50151,
              "host_port_end": 50151
            }
        ],
        "log_config": {
            "driver_config": {
                "type": "json-file",
                "max_files": 2,
                "max_size": "100M",
                "root_dir": ""
            },
            "mode_config": {
                "mode": "blocking",
                "max_buffer_size": ""
            }
        },
        "resources": null
    },
    "io_config": {
        "attach_stderr": false,
        "attach_stdin": false,
        "attach_stdout": false,
        "open_stdin": false,
        "stdin_once": false,
        "tty": false
    },
    "config": {
        "env": [
           "VEHICLEDATABROKER_DAPR_APP_ID=vehicledatabroker",
           "SDV_MQTT_ADDRESS=mosquitto:1883",
           "SDV_VEHICLEDATABROKER_ADDRESS=databroker:55555",
           "SDV_MIDDLEWARE_TYPE=native"
        ],
        "cmd": []
    },
    "network_settings": null,
    "state": {
        "pid": -1,
        "started_at": "",
        "error": "",
        "exit_code": 0,
        "finished_at": "",
        "exited": false,
        "dead": false,
        "restarting": false,
        "paused": false,
        "running": false,
        "status": "",
        "oom_killed": false
    },
    "created": "",
    "manually_stopped": false,
    "restart_count": 0
}

6 - Cloud2Device Messages

c2d-overview

In order to verify that the device and the cloud connector can successfully receive messages sent by the cloud backend, we will send a dummy message to the device.

Pre-Requisites

Validating configuration

First, let’s check that the cloud connection is active.

  • Login as root

  • Run sdv-health and check for the SDV Connectivity section:

    sdv-health
    

    sdv-health-connectivity

  • Start watching the output of the cloud connector:

    kantui
    
  • Select the cloud-connector container and press L to watch the logs

    Note: When an unknown type of message is received, the cloud connector will log an error:

    2022/04/13 16:04:41.911727  [agent]  ERROR  Handler returned error err="cannot deserialize cloud message: invalid character 'H' looking for beginning of value
    
  • Start watching on the MQTT message broker:

    mosquitto_sub -h localhost -t '#' --pretty -v
    

    Note: When a known type of message is received, the cloud connector will forward the message to the MQTT broker into the corresponding topic $appId/$cmdName

Sending a Device Message

  • Go to the Web Console of Azure IoT Hub
  • Select the device
  • Click on “Send Message”
  • Enter a C2D payload and click “Send”

Alternatively, on command line, use the Azure CLI client. Replace DeviceId and IotHubName with the appropriate names of your IoT Hub and device.

az iot device c2d-message send \
 --device-id ${DeviceID} \
 --hub-name ${IotHubName} \
 --data 'Hello World'