User Tools

Site Tools


Logging with a Trace Block

When working with realtime applications you have to be careful when logging. The formatting of any output into an output stream takes time and usually cannot be done with the full speed of the control system or the safety system. However, when debugging a intricate control algorithm it might be desirable to study various signal values together with their timestamps as they are calculated in the control system.

For this purpose you can place Trace blocks and log any signal into its internal ring buffer. The following example can be found under examples/controlSystem/traceTest. It has a simple control system with two trace blocks, each having a ring buffer lenght of 64.

  class ControlSystem {
    ControlSystem() : c(0.1), trace1(64), trace2(64) {
      i.setInitCondition(Vector3{0, 1.0, 2.0});
    Constant<Vector3> c;
    I<Vector3> i;
    Trace<Vector3> trace1, trace2;

An application can now run this control system. After stopping the executor you can output the content of the trace blocks onto any output stream, e.g. into a file. << "start writing file";
  std::ofstream file;"/tmp/ctrlData.txt", std::ios::trunc);
  timestamp_t* timeStampBuf = controlSystem.trace1.getTimestampTrace();
  Vector3* buf1 = controlSystem.trace1.getTrace();
  Vector3* buf2 = controlSystem.trace2.getTrace();
  for (int i = 0; i < controlSystem.trace1.getSize(); i++) file << timeStampBuf[i] << " " << buf1[i] << " " << buf2[i] << std::endl;
  file.close(); << "file written";

Enabling and Disabling

A trace block, though it will run by the control system, has to be enabled. If so, it will continously fill its trace buffer until it is disabled. This allows for tracing a given stream of signals. You could start tracing as soon as the safety system enters a certain safety level. You do this by starting the trace in the safety level action, see Safety Properties.
If you read out such a trace buffer it will simply return a buffer starting with the most recently logged at the end of the buffer. If the trace buffer was not filled completely, the returned buffer is simply as long as the trace buffer was actually filled.
A trace block has a given number of entries and is organized as a ring buffer. While running it fills new entries at the head and looses old entries at the tail.

Concurrent Writing of Trace Data

The example above shows how a trace buffer can be written to a file. This has to be done only after the executor stopped, because the writing usually takes a long time. If you wish to write a trace buffer to a file for further analysis while the application still runs, you can use a TraceWriter. This will do the writing in a separate thread and hence does not block the safety system or control system, see examples/controlSystem/traceTest. In this example the trace buffer gets written to a file every 30 seconds.

  TraceWriter<Vector3> tw(cs.trace2, "/tmp/ctrlData");
  if (slRunning.getNofActivations() % (int)(30 / period) == 0) { // write to log file every 30s

The name of the trace file will be appended with the current date and time.

tools/logger_trace/start.txt · Last modified: 2018/08/22 11:56 (external edit)