aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-connectivity/kuksa-val/kuksa-can-provider
diff options
context:
space:
mode:
authorScott Murray <scott.murray@konsulko.com>2024-06-18 16:30:05 -0400
committerScott Murray <scott.murray@konsulko.com>2024-06-20 02:04:33 +0000
commitec74e53f95f8a860bad8036ac492ae4a328f6b0c (patch)
tree3c3b64b2e165b6e265004b0f7a8c09972c0aefa1 /recipes-connectivity/kuksa-val/kuksa-can-provider
parentd9f9ddd177a57110d9c67ea438436c3160f8e429 (diff)
kuksa-dbc-feeder: Upgrade to kuksa-can-provider 0.4.3
Upgrade kuksa-dbc-feeder to 0.4.3 release, which includes a rename to kuksa-can-provider and a new location on github. All references to kuksa-dbc-feeder have been renamed throughout the tree to hopefully maintain consistency, though note that the actual script name upstream remains "dbcfeeder.py" at present. Bug-AGL: SPEC-5179 Change-Id: I88cf1c29187ad6cb882ee538a98e6e138aa76bfa Signed-off-by: Scott Murray <scott.murray@konsulko.com> Reviewed-on: https://gerrit.automotivelinux.org/gerrit/c/AGL/meta-agl-demo/+/30002 ci-image-build: Jenkins Job builder account ci-image-boot-test: Jenkins Job builder account Tested-by: Jenkins Job builder account
Diffstat (limited to 'recipes-connectivity/kuksa-val/kuksa-can-provider')
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/0001-dbc2val-add-installation-mechanism.patch77
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/0002-dbc2val-usability-improvements.patch80
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/0003-dbc2val-fix-token-file-configuration-option.patch32
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/0004-Enable-val2dbc-for-sensor-values.patch174
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/agl-vcar.dbc69
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/can-provider.json.token1
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/can-provider.token1
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/config.ini30
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/dbc_default_values.json40
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/kuksa-can-provider.default3
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/kuksa-can-provider.service12
-rw-r--r--recipes-connectivity/kuksa-val/kuksa-can-provider/mapping.yml152
12 files changed, 671 insertions, 0 deletions
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/0001-dbc2val-add-installation-mechanism.patch b/recipes-connectivity/kuksa-val/kuksa-can-provider/0001-dbc2val-add-installation-mechanism.patch
new file mode 100644
index 000000000..6a9c1ba14
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/0001-dbc2val-add-installation-mechanism.patch
@@ -0,0 +1,77 @@
+From 73dd680486b72d15d6f4c7aa129219ecdbcbd7b2 Mon Sep 17 00:00:00 2001
+From: Scott Murray <scott.murray@konsulko.com>
+Date: Wed, 19 Apr 2023 15:55:01 -0400
+Subject: [PATCH 1/4] dbc2val: add installation mechanism
+
+Add setup.py and setup.cfg to allow installing the dbcfeederlib
+module and dbcfeeder.py in a way suitable for packaging.
+
+Upstream-Status: pending
+
+Signed-off-by: Scott Murray <scott.murray@konsulko.com>
+---
+ setup.cfg | 30 ++++++++++++++++++++++++++++++
+ setup.py | 14 ++++++++++++++
+ 2 files changed, 44 insertions(+)
+ create mode 100644 setup.cfg
+ create mode 100644 setup.py
+
+diff --git a/setup.cfg b/setup.cfg
+new file mode 100644
+index 0000000..cb64407
+--- /dev/null
++++ b/setup.cfg
+@@ -0,0 +1,30 @@
++[metadata]
++name = dbcfeeder
++author = Sebastian Schildt, Naresh Nayak, Wenwen Chen
++author_email = sebastian.schildt@de.bosch.com, naresh.nayak@de.bosch.com, wenwen.chen@de.bosch.com
++description = KUKSA.val CAN provider
++long_description = file:README.md
++long_description_content_type = text/markdown
++url=https://github.com/eclipse-kuksa/kuksa-can-provider
++project_urls=
++ Source=https://github.com/eclipse-kuksa/kuksa-can-provider
++ Bug Tracker=https://github.com/eclipse-kuksa/kuksa-can-provider/issues
++classifiers =
++ Intended Audience :: Developers
++ Development Status :: 3 - Alpha
++ Environment :: Console
++ Programming Language :: Python :: 3
++ License :: OSI Approved :: Apache Software License
++ Operating System :: OS Independent
++ Topic :: Software Development
++
++license_file = LICENSE
++
++[options]
++python_requires = >=3.6
++install_requires=
++ pyserial
++ pyyaml
++ kuksa-client
++packages=dbcfeederlib
++scripts=dbcfeeder.py
+diff --git a/setup.py b/setup.py
+new file mode 100644
+index 0000000..c5fb2b7
+--- /dev/null
++++ b/setup.py
+@@ -0,0 +1,14 @@
++from setuptools import setup
++
++setup(
++ version_config={
++ "template": "{tag}",
++ "dev_template": "{tag}-{ccount}",
++ "dirty_template": "{tag}+{ccount}-dirty",
++ "starting_version": "0.1.0",
++ "version_callback": None,
++ "version_file": None,
++ "count_commits_from_version_file": False
++ },
++ setup_requires=['setuptools-git-versioning'],
++)
+--
+2.44.0
+
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/0002-dbc2val-usability-improvements.patch b/recipes-connectivity/kuksa-val/kuksa-can-provider/0002-dbc2val-usability-improvements.patch
new file mode 100644
index 000000000..91a309240
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/0002-dbc2val-usability-improvements.patch
@@ -0,0 +1,80 @@
+From 2e4e1f9147f1ebe5b545ae0cab41341e3abb00ae Mon Sep 17 00:00:00 2001
+From: Scott Murray <scott.murray@konsulko.com>
+Date: Sat, 15 Jun 2024 13:13:17 -0400
+Subject: [PATCH 2/4] dbc2val: usability improvements
+
+Changes:
+- Tweaked default configuration file search path to better match
+ Linux FHS. First look for a config.ini or dbc_feeder.ini in
+ /etc/kuksa-can-provider, then fall back to /etc/dbc_feeder.ini
+ before using trying other possible paths.
+- Add catching of exceptions around CAN device opening so that the
+ script can exit cleanly with an error message if the device is
+ not available.
+- Tweaked DBC default value file command line argument parsing so
+ that it does not attempt to fallback to "dbc_default_values.json"
+ in the current working directory. That likely works for upstream
+ test scenarios, but prevents running on a target.
+
+Upstream-Status: pending
+
+Signed-off-by: Scott Murray <scott.murray@konsulko.com>
+---
+ dbcfeeder.py | 19 +++++++++++++++----
+ 1 file changed, 15 insertions(+), 4 deletions(-)
+
+diff --git a/dbcfeeder.py b/dbcfeeder.py
+index a1ef174..c252503 100755
+--- a/dbcfeeder.py
++++ b/dbcfeeder.py
+@@ -144,7 +144,11 @@ class Feeder:
+ whitelisted_frame_ids.append(filter.can_id) # type: ignore
+ elm2canbridge.elm2canbridge(canport, self._elmcan_config, whitelisted_frame_ids)
+
+- self._reader.start()
++ try:
++ self._reader.start()
++ except:
++ log.error("Could not open %s, exiting", canport)
++ sys.exit(-1)
+
+ receiver = threading.Thread(target=self._run_receiver)
+ receiver.start()
+@@ -165,7 +169,12 @@ class Feeder:
+ # For now creating another bus
+ # Maybe support different buses for downstream/upstream in the future
+
+- self._canclient = CANClient(interface="socketcan", channel=canport, can_fd=can_fd)
++ self._canclient = None
++ try:
++ self._canclient = CANClient(interface="socketcan", channel=canport, can_fd=can_fd)
++ except:
++ log.error("Could not open %s, exiting", canport)
++ sys.exit(-1)
+
+ transmitter = threading.Thread(target=self._run_transmitter)
+ transmitter.start()
+@@ -335,8 +344,10 @@ def _parse_config(filename: str) -> configparser.ConfigParser:
+ configfile = filename
+ else:
+ config_candidates = [
+- "/config/dbc_feeder.ini",
++ "/etc/kuksa-can-provider/config.ini",
++ "/etc/kuksa-can-provider/dbc_feeder.ini",
+ "/etc/dbc_feeder.ini",
++ "/config/dbc_feeder.ini",
+ "config/dbc_feeder.ini",
+ ]
+ for candidate in config_candidates:
+@@ -534,7 +545,7 @@ def main(argv):
+ elif os.environ.get("DBC_DEFAULT_FILE"):
+ dbc_default = os.environ.get("DBC_DEFAULT_FILE")
+ else:
+- dbc_default = config.get(CONFIG_SECTION_CAN, CONFIG_OPTION_DBC_DEFAULT_FILE, fallback="dbc_default_values.json")
++ dbc_default = config.get(CONFIG_SECTION_CAN, CONFIG_OPTION_DBC_DEFAULT_FILE, fallback=None)
+
+ if args.mapping:
+ mappingfile = args.mapping
+--
+2.44.0
+
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/0003-dbc2val-fix-token-file-configuration-option.patch b/recipes-connectivity/kuksa-val/kuksa-can-provider/0003-dbc2val-fix-token-file-configuration-option.patch
new file mode 100644
index 000000000..faf94e0af
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/0003-dbc2val-fix-token-file-configuration-option.patch
@@ -0,0 +1,32 @@
+From 0b5822c6a8f8fa489bc7ae67f91284ac150f6518 Mon Sep 17 00:00:00 2001
+From: Scott Murray <scott.murray@konsulko.com>
+Date: Sat, 15 Jun 2024 13:18:08 -0400
+Subject: [PATCH 3/4] dbc2val: fix token file configuration option
+
+The client library changed the token option name to
+'token_or_tokenfile', update things to match so that token
+location can be configured again for dbcfeeder.py.
+
+Upstream-Status: pending
+
+Signed-off-by: Scott Murray <scott.murray@konsulko.com>
+---
+ dbcfeederlib/serverclientwrapper.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/dbcfeederlib/serverclientwrapper.py b/dbcfeederlib/serverclientwrapper.py
+index 498e1b6..fa43d28 100644
+--- a/dbcfeederlib/serverclientwrapper.py
++++ b/dbcfeederlib/serverclientwrapper.py
+@@ -56,7 +56,7 @@ class ServerClientWrapper(clientwrapper.ClientWrapper):
+ self._client_config["insecure"] = not self._tls
+ # Do not set token if it is empty to allow default client lib info to be used
+ if self._token_path != "":
+- self._client_config["token"] = self._token_path
++ self._client_config["token_or_tokenfile"] = self._token_path
+
+ if self._root_ca_path:
+ self._client_config['cacertificate'] = self._root_ca_path
+--
+2.44.0
+
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/0004-Enable-val2dbc-for-sensor-values.patch b/recipes-connectivity/kuksa-val/kuksa-can-provider/0004-Enable-val2dbc-for-sensor-values.patch
new file mode 100644
index 000000000..e45fb4cf5
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/0004-Enable-val2dbc-for-sensor-values.patch
@@ -0,0 +1,174 @@
+From 937218a357ac1914fe410cf3ad31a67d54a63270 Mon Sep 17 00:00:00 2001
+From: Scott Murray <scott.murray@konsulko.com>
+Date: Mon, 17 Jun 2024 17:07:44 -0400
+Subject: [PATCH 4/4] Enable val2dbc for sensor values
+
+Rework to allow val2dbc mode to write out sensor values in
+addition to actuator target values.
+
+Upstream-Status: pending
+
+Signed-off-by: Scott Murray <scott.murray@konsulko.com>
+---
+ dbcfeeder.py | 8 ++++++--
+ dbcfeederlib/databrokerclientwrapper.py | 18 ++++++++++++------
+ dbcfeederlib/dbc2vssmapper.py | 21 ++++++++++-----------
+ dbcfeederlib/serverclientwrapper.py | 2 +-
+ mapping/README.md | 2 --
+ 5 files changed, 29 insertions(+), 22 deletions(-)
+
+diff --git a/dbcfeeder.py b/dbcfeeder.py
+index c252503..c1e20c4 100755
+--- a/dbcfeeder.py
++++ b/dbcfeeder.py
+@@ -289,19 +289,23 @@ class Feeder:
+ log.debug("Processing %d VSS Data Entry updates", len(updates))
+ dbc_signal_names: Set[str] = set()
+ for update in updates:
++ value = None
+ if update.entry.value is not None:
+- # This should never happen as we do not subscribe to current value
+ log.warning(
+ "Current value for %s is now: %s of type %s",
+ update.entry.path, update.entry.value.value, type(update.entry.value.value)
+ )
++ value = update.entry.value.value
+
+ if update.entry.actuator_target is not None:
+ log.debug(
+ "Target value for %s is now: %s of type %s",
+ update.entry.path, update.entry.actuator_target, type(update.entry.actuator_target.value)
+ )
+- affected_signals = self._mapper.handle_update(update.entry.path, update.entry.actuator_target.value)
++ value = update.entry.actuator_target.value
++
++ if value != None:
++ affected_signals = self._mapper.handle_update(update.entry.path, value)
+ dbc_signal_names.update(affected_signals)
+
+ messages_to_send: Set[Message] = set()
+diff --git a/dbcfeederlib/databrokerclientwrapper.py b/dbcfeederlib/databrokerclientwrapper.py
+index 716ee6d..db2b80a 100644
+--- a/dbcfeederlib/databrokerclientwrapper.py
++++ b/dbcfeederlib/databrokerclientwrapper.py
+@@ -199,14 +199,20 @@ class DatabrokerClientWrapper(clientwrapper.ClientWrapper):
+ def supports_subscription(self) -> bool:
+ return True
+
+- async def subscribe(self, vss_names: List[str], callback):
++ async def subscribe(self, vss_entries: dict[str, str], callback):
+ """Create a subscription and invoke the callback when data received."""
+ entries: List[SubscribeEntry] = []
+- for name in vss_names:
+- # Always subscribe to target
+- subscribe_entry = SubscribeEntry(name, View.FIELDS, [Field.ACTUATOR_TARGET])
+- log.info("Subscribe entry: %s", subscribe_entry)
+- entries.append(subscribe_entry)
++ for name, signal_type in vss_entries.items():
++ if signal_type == "actuator":
++ subscribe_entry = SubscribeEntry(name, View.FIELDS, [Field.ACTUATOR_TARGET])
++ log.info("Subscribe entry: %s", subscribe_entry)
++ entries.append(subscribe_entry)
++ if signal_type == "sensor":
++ subscribe_entry = SubscribeEntry(name, View.FIELDS, [Field.VALUE])
++ log.info("Subscribe entry: %s", subscribe_entry)
++ entries.append(subscribe_entry)
++ if not entries:
++ return
+
+ # If there is a path VSSClient will request a secure connection
+ if self._tls and self._root_ca_path:
+diff --git a/dbcfeederlib/dbc2vssmapper.py b/dbcfeederlib/dbc2vssmapper.py
+index 218f693..2be5e98 100644
+--- a/dbcfeederlib/dbc2vssmapper.py
++++ b/dbcfeederlib/dbc2vssmapper.py
+@@ -69,12 +69,13 @@ class VSSMapping:
+ parser: Parser = Parser()
+
+ def __init__(self, vss_name: str, dbc_name: str, transform: dict, interval_ms: int,
+- on_change: bool, datatype: str, description: str):
++ on_change: bool, signal_type: str, datatype: str, description: str):
+ self.vss_name = vss_name
+ self.dbc_name = dbc_name
+ self.transform = transform
+ self.interval_ms = interval_ms
+ self.on_change = on_change
++ self.signal_type = signal_type
+ self.datatype = datatype
+ self.description = description
+ # For time comparison (interval_ms) we store last value used for comparison. Unit seconds.
+@@ -347,7 +348,7 @@ class Mapper(DBCParser):
+ if can_signal_name not in self._dbc2vss_mapping:
+ self._dbc2vss_mapping[can_signal_name] = []
+ mapping_entry = VSSMapping(expanded_name, can_signal_name, transformation_definition, interval, on_change,
+- node["datatype"], node["description"])
++ node["type"], node["datatype"], node["description"])
+ self._dbc2vss_mapping[can_signal_name].append(mapping_entry)
+
+ for msg_def in self.get_messages_for_signal(can_signal_name):
+@@ -398,7 +399,7 @@ class Mapper(DBCParser):
+ log.warning("Ignoring \"interval_ms\" property of mapping definition for %s", expanded_name)
+
+ mapping_entry = VSSMapping(expanded_name, can_signal_name, transform, interval, on_change,
+- node["datatype"], node["description"])
++ node["type"], node["datatype"], node["description"])
+ if can_signal_name not in self._vss2dbc_mapping:
+ self._vss2dbc_mapping[expanded_name] = []
+ self._vss2dbc_mapping[expanded_name].append(mapping_entry)
+@@ -426,12 +427,7 @@ class Mapper(DBCParser):
+ if dbc2vss_def is not None:
+ self._analyze_dbc2vss(expanded_name, node, dbc2vss_def)
+ if "vss2dbc" in node:
+- if node["type"] == "actuator":
+- self._analyze_vss2dbc(expanded_name, node, node["vss2dbc"])
+- else:
+- # vss2dbc is handled by subscription to target value, so only makes sense for actuators
+- log.error("vss2dbc only allowed for actuators, VSS signal %s is not an actuator!", expanded_name)
+- sys.exit(-1)
++ self._analyze_vss2dbc(expanded_name, node, node["vss2dbc"])
+
+ def _traverse_vss_node(self, name, node, prefix=""):
+ """
+@@ -474,9 +470,12 @@ class Mapper(DBCParser):
+ """Get all CAN signal names for which a mapping to a VSS Data Entry exists."""
+ return self._dbc2vss_mapping.keys()
+
+- def get_vss2dbc_entries(self) -> KeysView[str]:
++ def get_vss2dbc_entries(self) -> Dict[str, str]:
+ """Get all VSS Data Entry paths for which a mapping to a CAN signal name exists."""
+- return self._vss2dbc_mapping.keys()
++ entries: Dict[str, str] = {}
++ for name, mappings in self._vss2dbc_mapping.items():
++ entries[name] = mappings[0].signal_type
++ return entries
+
+ def get_vss_names(self) -> Set[str]:
+ """Get all VSS names used in mappings, both vss2dbc and dbc2vss"""
+diff --git a/dbcfeederlib/serverclientwrapper.py b/dbcfeederlib/serverclientwrapper.py
+index fa43d28..86b2ceb 100644
+--- a/dbcfeederlib/serverclientwrapper.py
++++ b/dbcfeederlib/serverclientwrapper.py
+@@ -122,6 +122,6 @@ class ServerClientWrapper(clientwrapper.ClientWrapper):
+ log.info("Feature not implemented")
+ return False
+
+- async def subscribe(self, vss_names: List[str], callback):
++ async def subscribe(self, vss_entries: dict[str, str], callback):
+ log.error("Feature not implemented")
+ return
+diff --git a/mapping/README.md b/mapping/README.md
+index 2155f28..ea6de07 100644
+--- a/mapping/README.md
++++ b/mapping/README.md
+@@ -44,8 +44,6 @@ This is built on the assumption that the DBC provider always send target values
+ Having separate configurations (`dbc2vss` and `vss2dbc`) is needed as wanted value and actual value never are sent
+ by the same DBC signal, they are not even part of the same CAN-frame.
+
+-*This means that `vss2dbc` only can be used for actuators, as only actuators have target values!*
+-
+ ## Example mapping files
+
+ Example mapping files for various VSS versions can be found in this folder.
+--
+2.44.0
+
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/agl-vcar.dbc b/recipes-connectivity/kuksa-val/kuksa-can-provider/agl-vcar.dbc
new file mode 100644
index 000000000..e638d1287
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/agl-vcar.dbc
@@ -0,0 +1,69 @@
+VERSION "AGL Virtual Car 1.0"
+
+BS_:
+
+BO_ 1001 Vehicle_Status_1: 8 Vector_XXX
+ SG_ PT_VehicleAvgSpeed : 7|15@0+ (0.015625,0) [0|0] "" Vector_XXX
+
+BO_ 985 Vehicle_Status_2: 8 Vector_XXX
+ SG_ PT_FuelLevelPct : 8|8@1+ (0.392157,0) [0|0] "" Vector_XXX
+ SG_ PT_EngineSpeed : 23|16@0+ (0.25,0) [0|0] "" Vector_XXX
+ SG_ PT_FuelLevelLow : 55|1@1+ (1,0) [0|1] "" Vector_XXX
+
+BO_ 986 Vehicle_Status_3: 8 Vector_XXX
+ SG_ PT_HazardOn : 0|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ PT_LeftTurnOn : 1|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ PT_RightTurnOn : 2|1@1+ (1,0) [0|1] "" Vector_XXX
+
+BO_ 48 HVAC_Control_1: 8 Vector_XXX
+ SG_ PT_TempLeft : 7|8@0+ (0.4166666667,0) [0|100] "C" Vector_XXX
+ SG_ PT_TempRight : 15|8@0+ (0.4166666667,0) [0|100] "C" Vector_XXX
+ SG_ PT_FanSpeed : 39|8@0+ (0.392157,0) [0|100] "%" Vector_XXX
+
+BO_ 401 Engine: 8 Vector__XXX
+ SG_ ThrottlePosition : 63|8@0+ (0.392157,0) [0|100.000035] "%" Vector__XXX
+
+BO_ 381 ABS: 8 Vector__XXX
+ SG_ VehicleSpeed : 7|12@0+ (0.0625,0) [0|255.9375] "km / h" ECM_HS,BCM_HS
+ SG_ SteeringPosition : 23|12@0+ (0.0439453125,-90) [-90|89.9560546875] "deg" ECM_HS,BCM_HS
+ SG_ BrakePressure : 39|8@0+ (75,0) [0|19125] "kPa" ECM_HS,BCM_HS
+
+BO_ 532 Transmission: 8 Vector__XXX
+ SG_ Gear : 7|8@0+ (1,-1) [-1|127] "" ECM_HS,BCM_HS
+
+BO_ 533 Airbag: 8 Vector__XXX
+ SG_ CollisionIntensity : 7|12@0+ (24.4140625,0) [0|100000] "N" ECM_HS,BCM_HS
+
+BO_ 534 IMU1: 8 Vector__XXX
+ SG_ AccelerationX : 7|12@0+ (0.48828125,-1000) [-1000|1000] "m/s^2" ECM_HS,BCM_HS
+ SG_ AccelerationY : 23|12@0+ (0.48828125,-1000) [-1000|1000] "m/s^2" ECM_HS,BCM_HS
+ SG_ AccelerationZ : 39|12@0+ (0.48828125,-1000) [-1000|1000] "m/s^2" ECM_HS,BCM_HS
+
+BO_ 535 IMU2: 8 Vector__XXX
+ SG_ GyroscopeX : 7|12@0+ (0.48828125,-1000) [-1000|1000] "rad/s" ECM_HS,BCM_HS
+ SG_ GyroscopeY : 23|12@0+ (0.48828125,-1000) [-1000|1000] "rad/s" ECM_HS,BCM_HS
+ SG_ GyroscopeZ : 39|12@0+ (0.48828125,-1000) [-1000|1000] "rad/s" ECM_HS,BCM_HS
+
+BO_ 536 GNSS: 8 Vector__XXX
+ SG_ Latitude : 7|32@0+ (0.0000000419095158577,-90) [-90|90] "deg" ECM_HS,BCM_HS
+ SG_ Longitude : 39|32@0+ (0.00000008381903171539,-180) [-180|180] "deg" ECM_HS,BCM_HS
+
+BO_ 33 Steering_Wheel: 8 Vector_XXX
+ SG_ SW_Previous : 39|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_VolumeUp : 38|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_Mode : 37|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_VolumeDown : 36|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_Next : 35|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_Info : 33|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_VolumeMute : 32|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_Voice : 42|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_PhoneHangup : 41|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_PhoneCall : 40|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_CruiseEnable : 55|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_CruiseResume : 54|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_CruiseSet : 52|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_CruiseCancel : 51|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_CruiseLimit : 49|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_CruiseDistance : 48|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_Horn : 63|1@1+ (1,0) [0|1] "" Vector_XXX
+ SG_ SW_LaneDepartureWarning : 56|1@1+ (1,0) [0|1] "" Vector_XXX
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/can-provider.json.token b/recipes-connectivity/kuksa-val/kuksa-can-provider/can-provider.json.token
new file mode 100644
index 000000000..b8df66663
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/can-provider.json.token
@@ -0,0 +1 @@
+eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJrdWtzYS52YWwiLCJpc3MiOiJFY2xpcHNlIEtVS1NBIERldiIsImFkbWluIjp0cnVlLCJtb2RpZnlUcmVlIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTc2NzIyNTU5OSwia3Vrc2EtdnNzIjp7IioiOiJydyJ9fQ.p2cnFGH16QoQ14l6ljPVKggFXZKmD-vrw8G6Vs6DvAokjsUG8FHh-F53cMsE-GDjyZH_1_CrlDCnbGlqjsFbgAylqA7IAJWp9_N6dL5p8DHZTwlZ4IV8L1CtCALs7XVqvcQKHCCzB63Y8PgVDCAqpQSRb79JPVD4pZwkBKpOknfEY5y9wfbswZiRKdgz7o61_oFnd-yywpse-23HD6v0htThVF1SuGL1PuvGJ8p334nt9bpkZO3gaTh1xVD_uJMwHzbuBCF33_f-I5QMZO6bVooXqGfe1zvl3nDrPEjq1aPulvtP8RgREYEqE6b2hB8jouTiC_WpE3qrdMw9sfWGFbm04qC-2Zjoa1yYSXoxmYd0SnliSYHAad9aXoEmFENezQV-of7sc-NX1-2nAXRAEhaqh0IRuJwB4_sG7SvQmnanwkz-sBYxKqkoFpOsZ6hblgPDOPYY2NAsZlYkjvAL2mpiInrsmY_GzGsfwPeAx31iozImX75rao8rm-XucAmCIkRlpBz6MYKCjQgyRz3UtZCJ2DYF4lKqTjphEAgclbYZ7KiCuTn9HualwtEmVzHHFneHMKl7KnRQk-9wjgiyQ5nlsVpCCblg6JKr9of4utuPO3cBvbjhB4_ueQ40cpWVOICcOLS7_w0i3pCq1ZKDEMrYDJfz87r2sU9kw1zeFQk \ No newline at end of file
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/can-provider.token b/recipes-connectivity/kuksa-val/kuksa-can-provider/can-provider.token
new file mode 100644
index 000000000..8ce854f34
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/can-provider.token
@@ -0,0 +1 @@
+eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJsb2NhbCBkZXYiLCJpc3MiOiJjcmVhdGVUb2tlbi5weSIsImF1ZCI6WyJrdWtzYS52YWwiXSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3NjcyMjU1OTksInNjb3BlIjoicHJvdmlkZSJ9.OJWzTvDjcmeWyg3vmBR5TEtqYaHq8HrpFLlTKZAfDBAQBUHpyUEboJ97jfWuWgBnTpnfboyfAbwvLqo6bEVZ6tXzF8n9LtW6HmPbIWoDqXuobM2grUCVaGKuOcnCpMCQYChziqHbYwRJYP9nkYgbQU1kE4dN7880Io4xzq0GEbWksB2CVpOoExQUmCZpCohPs-XEkdmXhcUKnWnOeiSsRGKusx987vpY_WOXh6WE7DfJgzAgpPDo33qI7zQuTzUILORQsiHmsrQO0-zcvokNjaQUzlt5ETZ7MQLCtiUQaN0NMbDMCWkmSfNvZ5hKCNbfr2FaiMzrGBOQdvQiFo-DqZKGNweaGpufYXuaKfn3SXKoDr8u1xDE5oKgWMjxDR9pQYGzIF5bDXITSywCm4kN5DIn7e2_Ga28h3rBl0t0ZT0cwlszftQRueDTFcMns1u9PEDOqf7fRrhjq3zqpxuMAoRANVd2z237eBsS0AvdSIxL52N4xO8P_h93NN8Vaum28fTPxzm8p9WlQh4mgUelggtT415hLcxizx15ARIRG0RiW91Pglzt4WRtXHnsg93Ixd3yXXzZ2i4Y0hqhj_L12SsXunK2VxKup2sFCQz6wM-t_7ADmNYcs80idzsadY8rYKDV8N1WqOOd4ANG_nzWa86Tyu6wAwhDVag5nbFmLZQ \ No newline at end of file
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/config.ini b/recipes-connectivity/kuksa-val/kuksa-can-provider/config.ini
new file mode 100644
index 000000000..f41fb87a5
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/config.ini
@@ -0,0 +1,30 @@
+[general]
+# server type:
+# switch between kuksa_databroker and kuksa_val_server
+server_type = kuksa_databroker
+# VSS mapping file
+mapping = /usr/share/vss/vss.json
+
+# IP address for server (KUKSA.val Server or Databroker)
+ip = localhost
+
+# Port for server (KUKSA.val Server or Databroker)
+port = 55555
+
+# Shall TLS be used (default False for Databroker, True for KUKSA.val Server)
+tls = True
+
+# TLS-related settings
+# Path to root CA, needed if using TLS
+root_ca_path=/etc/kuksa-val/CA.pem
+
+# Token file for authorization.
+token = /etc/kuksa-can-provider/can-provider.token
+
+[can]
+# CAN port
+port = can0
+# Enable SAE-J1939 Mode. False: ignore
+j1939 = False
+# DBC file used to parse CAN messages
+dbcfile = /etc/kuksa-can-provider/agl-vcar.dbc
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/dbc_default_values.json b/recipes-connectivity/kuksa-val/kuksa-can-provider/dbc_default_values.json
new file mode 100644
index 000000000..0562569db
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/dbc_default_values.json
@@ -0,0 +1,40 @@
+{
+ "PT_VehicleAvgSpeed" : 0,
+ "PT_FuelLevelPct" : 0,
+ "PT_EngineSpeed" : 0,
+ "PT_FuelLevelLow" : 0,
+ "PT_TempLeft" : 0,
+ "PT_TempRight" : 0,
+ "PT_FanSpeed" : 0,
+ "ThrottlePosition" : 0,
+ "VehicleSpeed" : 0,
+ "SteeringPosition" : 0,
+ "BrakePressure" : 0,
+ "Gear" : 0,
+ "AccelerationX" : 0,
+ "AccelerationY" : 0,
+ "AccelerationZ" : 0,
+ "GyroscopeX" : 0,
+ "GyroscopeY" : 0,
+ "GyroscopeZ" : 0,
+ "Latitude" : 0,
+ "Longitude" : 0,
+ "SW_Previous" : 0,
+ "SW_VolumeUp" : 0,
+ "SW_Mode" : 0,
+ "SW_VolumeDown" : 0,
+ "SW_Next" : 0,
+ "SW_Info" : 0,
+ "SW_VolumeMute" : 0,
+ "SW_Voice" : 0,
+ "SW_PhoneHangup" : 0,
+ "SW_PhoneCall" : 0,
+ "SW_CruiseEnable" : 0,
+ "SW_CruiseResume" : 0,
+ "SW_CruiseSet" : 0,
+ "SW_CruiseCancel" : 0,
+ "SW_CruiseLimit" : 0,
+ "SW_CruiseDistance" : 0,
+ "SW_Horn" : 0,
+ "SW_LaneDepartureWarning" : 0
+}
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/kuksa-can-provider.default b/recipes-connectivity/kuksa-val/kuksa-can-provider/kuksa-can-provider.default
new file mode 100644
index 000000000..aad0bc419
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/kuksa-can-provider.default
@@ -0,0 +1,3 @@
+# For output only mode:
+#EXTRA_ARGS="--val2dbc --no-dbc2val --dbc-default /etc/kuksa-can-provider/dbc_default_values.json"
+#LOG_LEVEL=debug
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/kuksa-can-provider.service b/recipes-connectivity/kuksa-val/kuksa-can-provider/kuksa-can-provider.service
new file mode 100644
index 000000000..41258d275
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/kuksa-can-provider.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Eclipse KUKSA.val DBC feeder
+Requires=kuksa-databroker.service can-dev-helper.service
+After=kuksa-databroker.service can-dev-helper.service
+
+[Service]
+EnvironmentFile=-/etc/default/kuksa-can-provider
+ExecStart=/usr/bin/dbcfeeder.py $EXTRA_ARGS
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/recipes-connectivity/kuksa-val/kuksa-can-provider/mapping.yml b/recipes-connectivity/kuksa-val/kuksa-can-provider/mapping.yml
new file mode 100644
index 000000000..b1c1fece7
--- /dev/null
+++ b/recipes-connectivity/kuksa-val/kuksa-can-provider/mapping.yml
@@ -0,0 +1,152 @@
+# Mapping Speed
+PT_VehicleAvgSpeed:
+ minupdatedelay: 100
+ targets:
+ Vehicle.Speed: {}
+
+PT_EngineSpeed:
+ minupdatedelay: 100
+ targets:
+ Vehicle.Powertrain.CombustionEngine.Speed:
+ transform:
+ math: "floor(x+0.5)"
+
+#
+# NOTE:
+#
+# The following mappings depend on the AGL-specific VSS overlay
+# that adds the extra Vehicle.Cabin.SteeringWheel.Switches sensors.
+# Since the CAN events are coming from LIN polling, applications
+# need to filter/debounce themselves. The minupdatedelay of 0 is
+# intentional to avoid missing events.
+#
+
+SW_Next:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.Next:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_Previous:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.Previous:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_Mode:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.Mode:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_Info:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.Info:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_CruiseEnable:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.CruiseEnable:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_CruiseSet:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.CruiseSet:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_CruiseResume:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.CruiseResume:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_CruiseCancel:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.CruiseCancel:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_VolumeUp:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.VolumeUp:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_VolumeDown:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.VolumeDown:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_VolumeMute:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.VolumeMute:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_Horn:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.Horn:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"
+
+SW_LaneDepartureWarning:
+ minupdatedelay: 0
+ targets:
+ Vehicle.Cabin.SteeringWheel.Switches.LaneDepartureWarning:
+ filter-duplicates: "true"
+ transform:
+ fullmapping:
+ 0: "false"
+ 1: "true"