Reporting

This tutorial will cover using vspyx.Reporting.Module to easily create reports in Vehicle Spy X.

Setup

This tutorial will build on the work of Getting signal values. If you have not completed that tutorial, a finished example is availble at its bottom.

We will use numpy and matplotlib for numerical processing generating plots. These packages can be installed with pip.

pip3 install matplotlib numpy

Creating an empty report

A new report is created by calling vspyx.Reporting.Module.NewReport().

report = app.Reporting.NewReport()
report.Title = "My Report"

Adding content

For the report to be useful, we need to add some content to it. Referring back to Getting signal values, we will add a new section to the report for every signal that was individually requested. We will then append to a list all values for the signal, giving us the ability to process these after the entire buffer is finished. Every report has a vspyx.Reporting.Report.RootSection. New subsections can be added by calling vspyx.Reporting.Section.NewSection().

# at global scope
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as pyplot
pyplot.ioff()
import numpy

signal_points = {}

# in on_trace_point
if point.Traceable not in signal_points:
  signal_points[point.Traceable] = [point]
else:
  signal_points[point.Traceable].append(point)

# before looping over all signals
sections = {}

# after trace.OnPoint.Add(on_trace_point)
sections[signal] = report.RootSection.NewSection()
sections[signal].Name = signal_id

We now have a report section defined for each signal, as well as all of the signal points. Here, we will calculate some statistics on the each signal with numpy and use matplotlib to generate a plot.

# after app.VehicleSpy.Stop()

for signal, points in signal_points.items():
  timestamps = [(x.Timestamp - points[0].Timestamp).total_seconds() for x in points]
  values = [x.PhysicalValue for x in points]

  min = numpy.min(values)
  max = numpy.max(values)
  std_dev = numpy.std(values)
  average = numpy.average(values)

  stats = sections[signal].NewTable()
  stats.SetHeader(["Statistic", "Value"])
  stats.AddRow(["min", str(min)])
  stats.AddRow(["max", str(max)])
  stats.AddRow(["average", str(average)])
  stats.AddRow(["std dev", str(std_dev)])

  pyplot.clf()
  _, axis = pyplot.subplots()
  axis.plot(timestamps, values, label=signal.ID)
  axis.set_ylabel(signal.ID)
  axis.set_xlabel("Time (s)")
  pyplot.savefig(f"{signal.ID}.png", format='png')
  sections[signal].NewImage(f"{signal.ID}.png")

Generating the report

vspyx.Reporting.Report has two functions for generating the actual report: vspyx.Reporting.Report.BuildHTML() and vspyx.Reporting.Report.BuildPDF(). Since generating the report may take some time, these functions immediately return a vspyx.Core.Task_bool_t. This task can either by synchronously executed, or ran in the background.

report.BuildHTML("report.html").Execute()
report.BuildPDF("report.pdf").Execute()

Example output

My Report

RPM

Statistic Value
min 0
max 3293
average 2660.7385486361295
std dev 432.96441048025554
_images/RPM.png