I think the syntax you described for value extraction specification is generally sensible, although some minor details could be, perhaps, improved slightly. In general, I would recommend keeping the syntax somewhat reminiscent of DSDL (relevant: https://github.com/UAVCAN/specification/pull/46). I came up with a few examples:
123:uavcan.node.Heartbeat.0.1.health
– node ID 123, data type uavcan.node.Heartbeat
version 0.1, field health
. The port ID is not specified, which means that we need to use the fixed port ID. If there was no fixed port ID defined with this data type, it would be a semantic error.
456:uavcan.register.Access.1.0(name="temperature").value.real32[0]@10Hz
– node ID 456, data type uavcan.register.Access.1.0
. This is a service type, so we must specify the request object, which is described as (name="temperature")
. If the request schema contains composite type fields, things get complex: (baz=vendor.MyCompositeType.1.0(foo=123.456, bar=[1, 2, 3]))
, but it’s still sensible and so far I don’t see a simpler solution. If there are no fields to specify for the request object, the form is reduced to a pair of empty parens ()
; essentially this is to look like RPC (remote procedure call). Since this is a service type, we’ll never get any values until we explicitly ask for them, so we must specify the polling rate via @10Hz
; the syntax is questionable though. The polling rate could default to 1 Hz if not specified.
The rate specifier like @10Hz
could also be generalized to messages for rate limiting purposes, but this is probably not very useful in general.
We could incorporate filter expressions into this format later; for now it’s important to agree on the general syntax. The expressions like above essentially denote the concept of a “remote value”, which can be used flexibly to define visualization configurations and just about anything network-value-related in general. I imagine we could use a simple YAML schema for describing visualization configurations in particular:
visualizers:
American temperature plot: # Keys contain human-readable titles to be displayed in the GUI
type: time-y
y: # The expressions convert values to Fahrenheit
- (456:uavcan.register.Access.1.0(name="temperature.device").value.real32[0]@10Hz - 273.15) * 9/5 + 32
- (456:uavcan.register.Access.1.0(name="temperature.outside").value.real32[0]@10Hz - 273.15) * 9/5 + 32
y-right: # "right" means the right-side Y axis, which is optional
# Electrical power in kW:
- 123:5678.uavcan.si.electric_current.Scalar.1.0.ampere * 456:5678.uavcan.si.voltage.Scalar.1.0.volt * 1e-3
Camera image viewer:
type: image
image: 123:5678.vendor.camera.Frame1280x1024.1.0.pixels
width: 123:5678.vendor.camera.Frame1280x1024.PIXELS_PER_ROW
pixel_format: RGB888
Simple position:
type: x-y
style: line
palette: jet
thickness: 3px
x: 1234:5678.uavcan.si.length.WideVector3.meter[0]
y: 1234:5678.uavcan.si.length.WideVector3.meter[1]
color: 1234:5678.uavcan.si.length.WideVector3.meter[2]
Satellites:
type: histogram
bin_size: 1
value: 123:5678.uavcan.primitive.scalar.Integer8.1.0.value
(would you agree that “visualizer” might be a better word than “plotter”?)
In terms of the three-stage pipeline model I described here earlier, a “remote value specifier” string (like 1234:5678.uavcan.si.length.WideVector3.meter[2]
) contains specification for the first two stages of the pipeline, and the YAML above specifies the last one.
The backend should be able to identify value specifiers that refer to the same object and reuse subscribers/service clients as much as possible to avoid resource overutilization. For example, 1234:5678.uavcan.si.length.WideVector3.meter[0]
is repeated thrice where only the index differs; maintaining a dedicated subscription per value makes no sense here.