Skip to main content

Named values in RemotiveStudio and Python

· 5 min read

A gear lever signal that reads 3 doesn't tell you much. Your signal database usually knows that 3 means DRIVE, and RemotiveStudio and the Python libraries can use that knowledge. This post shows where named values live and how to work with them in both.

What named values are​

Many signals don't carry a measurement. They carry a state: a door is open or closed, a gear is in park or drive, a lamp is off, on or failing. On the bus these states are plain integers, and the signal database maps each integer to a name. In a DBC file this is the value table for the signal; ARXML and LDF have their own ways of describing the same thing. Think of it as an enum attached to a signal.

RemotiveLabs calls these mappings named values. They come straight from the signal databases your topology references (DBC, ARXML, LDF, Fibex, see the supported file formats), so you don't define them anywhere else. If the database has them, you can use them.

Finding named values in RemotiveStudio​

The quickest way to see what a database defines is the Signal view in RemotiveStudio:

  1. Open the file tree with Files and click your .instance.yaml, .platform.yaml, or a database file directly.
  2. Click the bar-chart icon in the segmented control at the top of the topology viewer.
  3. Find the frame you care about. The search bar filters both frames and signals.
  4. Click the frame row to expand its signals. Each signal shows its unit, min/max, factor and offset, receivers, and its named values.

The first time you open the Signal view, RemotiveStudio indexes the signal databases, so give it a moment on large ARXML files. Today the Signal view covers CAN and LIN signals.

This is also a good way to check a database before you write code against it. If you expect GearPosition to have named values and the Signal view shows none, the database you're using doesn't have them, and no code will fix that.

Reading named values from Python​

The RemotiveBroker Python API exposes the mapping on every SignalInfo in two directions: named_values maps raw integers to names, and value_names maps names back to integers. Here is a small script, adapted from the API docs, that prints both for one signal. The DriverCan namespace and the GearData.GearPosition signal are examples; use your own.

import asyncio
from remotivelabs.broker import BrokerClient

async def main() -> None:
async with BrokerClient(url="http://127.0.0.1:50051") as broker_client:
info = await broker_client.get_signal_info("GearData.GearPosition", "DriverCan")
if info is None:
raise SystemExit("Signal not found in DriverCan")
print(info.named_values) # raw value -> name, e.g. {0: "PARK", 3: "DRIVE"}
print(info.value_names) # name -> raw value, e.g. {"PARK": 0, "DRIVE": 3}

if __name__ == "__main__":
asyncio.run(main())

If you're not sure which namespaces exist on a running topology, list them first:

remotive broker signals namespaces --url http://localhost:50051

When you subscribe, you can have the client decode values for you. Pass decode_named_values=True to subscribe_frames, and integer values that have a mapping arrive as their names in frame.signals:

stream = await broker_client.subscribe_frames(
("DriverCan", ["GearData"]),
decode_named_values=True,
)
async for frame in stream:
print(frame.signals) # {"GearData.GearPosition": "DRIVE", ...}

To write a state, look up the raw value with value_names and publish that integer:

from remotivelabs.broker import WriteSignal

await broker_client.publish(
("DriverCan", [WriteSignal(name="GearData.GearPosition", value=info.value_names["PARK"])]),
)

The same lookup works when you set restbus signals with RestbusSignalConfig.set(...). If you write behavioral models or tests with the RemotiveTopology framework, you're using the same broker client underneath, so SignalInfo.named_values and value_names are available there too.

Common pitfalls​

  • The value type changes. With decode_named_values=True, a signal that used to be an int becomes a str. Code that compares with == 3 or does arithmetic on the value silently stops matching. Pick one style per script and stick to it.
  • Not every value has a name. Only integers that appear in the mapping are decoded. A value outside the table, such as a reserved or error code, still arrives as an int, so handle both types, or look values up yourself with info.named_values.get(value, value).
  • Decoding is off by default. subscribe_frames doesn't decode unless you ask, and the frame handlers in the RemotiveTopology framework take a decode_named_values argument that is also off by default. If you see numbers where you expected names, check this first.
  • Names belong to a database. Two databases can map the same signal differently, for example a channel DBC and an ECU extract. Check the Signal view for the topology you actually run, and look names up with value_names instead of hard-coding the integers.
  • Typos fail late. info.value_names["Park"] raises a KeyError when the database says PARK. Copy names from the Signal view rather than from memory.

Named values are a small feature, but they make logs, dashboards and test assertions much easier to read. For more on the APIs, see APIs and client libraries.

About the author

Ola Nilsson

Builds the RemotiveLabs developer experience, from the docs and this blog to the tools around them. Usually found turning a support question into a guide, a guide into an example, and an example into a test that runs in CI. Has opinions about keyboard shortcuts.