Keywords

figure a
figure b

1 Introduction

Runtime monitoring is an applied formal method that assures the safety of a running system by evaluating its behavior against a formal specification. In the stream-based approach, this specification is given in terms of equations that relate input streams, that contain raw data such as sensor readings, to output streams that transform and aggregate the incoming information. The values on the output streams are then checked against trigger conditions that indicate faulty or dangerous situations.

This tutorial provides a comprehensive introduction to the RTLola monitoring framework. RTLola is the real-time extension [13] of the Lola specification language [8], which pioneered the stream-based approach. RTLola has been successfully applied to cyber-physical systems such as (unmanned) aircraft. Major case studies include the DLR ARTIS (Autonomous Rotorcraft Testbed for Intelligent Systems) family of aircraft developed at the German Aerospace Center (DLR) [4], and the fully-electric aircraft designed by Volocopter, a leading aircraft manufacturer of electric multi-rotor helicopters [2].

In the tutorial, we develop an RTLola specification for a real-world detect and avoid problem from the aerospace domain. We consider an autonomous drone flying in a shared airspace. The task of the monitor is to detect aircraft in the vicinity of the drone that might interfere with the drone’s flight path. We will develop the specification in multiple steps, starting with the simple case of a single non-moving object. Our final specification will handle an apriori unbounded number of independently moving entities. Along the way, we introduce the relevant RTLola concepts and some fundamental background.

There are two possible ways to read this tutorial. If this is the reader’s first encounter with RTLola, we recommend focussing on the development of the detect and avoid example. The tutorial starts in Sect. 2 with an overview of the monitoring framework and the running example. Afterwards, Sect. 3, Sect. 4, Sect. 5 and Sect. 6 extend the specification step-by-step, each section building up on the previous one. Finally, Sect. 7 explains how a monitor generated from a specification can be integrated into an existing system.

For readers interested in understanding the RTLola approach at a deeper technical level, the tutorial contains subsections with additional background. These subsections are marked as for experts to indicate that the subsections can safely be skipped at first reading. In this spirit, the for experts subsection of Sect. 2 provides a comprehensive overview of the various backends available in the RTLola framework; in Sect. 3, we discuss the static analysis of RTLola specifications. In Sect. 4, we introduce the type system, which is further refined in Sect. 5. In Sect. 6, we discuss finer points of parameterized specifications.

The tutorial is best experienced when following along in a browser window using the interactive RTLola Playground [16], which is available online [15]. Section 7 briefly explains how this tutorial is integrated into the Playground.

Related Work. There is a rich literature on runtime verification; we refer the reader to several introductory articles (cf. [1, 11, 19]). A previous tutorial on runtime monitoring has focussed on writing monitors using aspect-oriented programming [10]. Tutorials on stream-based monitoring have appeared as presentations at conferences (cf. [20, 22]), but this is, to the best of our knowledge, the first tutorial paper that includes a hands-on development of a stream-based specification for a real-life application scenario. While the paper is based on the RTLola framework [13], the fundamental concepts apply in similar form to other stream-based monitoring approaches. Notable other stream-based monitoring approaches, in addition to Lola [8] and its successor Lola2.0 [12], are the Tessla [7] and Striver [18] tools.

2 Overview

Runtime monitoring checks the behavior of a system, such as cyber-physical system (CPS), at runtime. The monitor receives input data from the system and analyses the data according to a formal specification. At each step, the monitor outputs if the specification is violated such that the system or an operator of the system can initiate countermeasures to return the system to a safe state. The next subsection introduces the general idea of stream-based monitoring followed by an introduction of the running example used in this tutorial.

Fig. 1.
figure 1

An overview over stream-based runtime monitoring.

Stream-Based Monitoring

Stream-based runtime monitoring interprets the system’s input data as streams, i.e. a temporal discrete sequence over rich data values, transforms these input streams into output streams and expresses violations in the system’s behavior. Figure 1a illustrates this idea: On the left side of the figure is the monitored system, which is in our case a drone. This system emits a sequence of timed values, which are called input streams. Stream equations then transform these input streams into output streams. Intuitively, stream equations are comparable to variable assignments in imperative programming languages. Yet, instead of only being evaluated once, like variable assignments, they are applied at every stream position to filter incoming data, compare values from different streams, or express complex temporal properties. Stream-based specification languages provide different stream access functions to reason about input streams (see Fig. 1b) and other output streams (see Fig. 1c). Stream equations can also access past values of a stream (see Fig. 1d) to express temporal properties. Finally, special boolean-valued output streams, called trigger streams, express violations in the system’s behavior.

The next section introduces some syntax and semantics of stream equations in more detail, but first, we outline this tutorial’s running example.

Fig. 2.
figure 2

A drone monitoring surrounding vehicles.

2.1 Running Example

During this tutorial, we build a specification within the aerospace domain inspired by the Detect and Avoid specification introduced by Baumeister et al. [2]. More concretely, we consider an autonomous drone flying in a shared airspace and describe the detection of surrounding vehicles that might interfere with the drone’s flight path. Figure 2 illustrates this property: The plane in the figure’s center represents the drone’s current position and the rays around the plane, the distance to other participants in the airspace. The exclamation mark indicates that this participant is close to the drone and is still approaching, so the drone should change the flight path to avoid a crash. From now on, we will call these participants intruders since they might interfere with the drone’s flight path.

Algorithmically, the monitor should perform the following operations to monitor this scenario:

  1. 1.

    Distance: The distance to each intruder should be computed whenever the intruder or the drone moves.

  2. 2.

    Closer: To check if a specific intruder is approaching the drone, the monitor has to calculate whether the distance of the intruder is decreasing.

  3. 3.

    Trigger: If this approach continues over five seconds and the intruder is close to the drone, the monitor should notify the drone to initialize a counteraction.

  4. 4.

    Stale: The monitor should check if the intruder positions are regularly updated. Otherwise, it is declared out-of-range.

In this scenario, we expect the drone to provide the monitor with two sensor readings representing the input streams in our specification. First, we assume that the system has a global navigation satellite system (GNSS) to provide the monitor with the drone’s latitude and longitude. Additionally, we assume that the system can detect other vehicles by sharing their position with other participants or by actively searching for intruders, e.g., with a radar, to get the latitude and longitude of the intruders. As output, the monitor provides the current distance to each intruder and the trigger if an approaching intruder is nearby.

The following sections explain different specifications describing these requirements in RTLola in a step-by-step fashion. We start with an intruder with a fixed position, e.g., a tree. Then, the specification is adjusted to handle a moving intruder, e.g., a single moving drone. Finally, we adapt the specification to detect more than one approaching vehicle.

Fig. 3.
figure 3

Overview of the RTLola framework.

For Experts: Integration & Compilation

The RTLola specification language is embedded in an extensive framework to analyze and monitor specifications. Figure 3 provides an overview of this framework.

It is divided into the frontend and several backends. The frontend takes a specification file and produces an intermediate representation. This representation contains an abstract syntax tree of the specification annotated with additional information relevant to the backends. Additionally, the frontend optimizes the specification as presented in [3]. To verify the functional correctness of the specification, the intermediate representation can be inspected before executing the monitor, as shown in [9].

All backends have online and offline monitoring capabilities, i.e., they can monitor a system at runtime or monitor a log of its execution. RTLola specifications can be executed in the software-based interpreter [13] or compiled into the hardware description language VHDL [5] or imperative programming languages [17]. Providing both an interpretation and compilation ensures flexibility and efficiency: An interpretation allows for easy debugging and quick development times of the specification as it can easily be adjusted and reevaluated. A compilation, particularly a compilation to hardware, can provide highly optimized monitors that meet strict system requirements such as a low power consumption. The framework’s versatility was confirmed in industrial case studies [2] with aerospace partners.

In the RTLola framework, the interpreter takes the specification as its intermediate representation and interprets it based on the incoming data from the system. It provides an extensive API to integrate it into existing implementations. Through the API, the interpreter can quickly adapt to different input data formats and sources.

The compilation takes the intermediate representation and produces executable code that implements a monitor for the given specification. For software, it produces code in an imperative programming language such as Rust. The hardware compiler produces VHDL code that can then be synthesized onto an FPGA. The monitor implementation receives inputs through input wires, and the current stream values are stored on the corresponding output wires or variables. After implementing the communication between the system and the monitor, it can be deployed with the system. Although this approach is less flexible than the interpretation, the resulting monitor is highly efficient once built and integrated.

3 Stream-Based Specifications

After Sect. 2 has introduced the general idea of streams and stream equations, this section presents the concrete syntax of RTLola and gives the first concrete examples. Output streams are declared using the keyword followed by a stream expression describing the computation of a stream value. In comparison, input streams are declared using the keyword and do not require a stream expression as their value is given by the system under observation. Trigger streams, special output streams with boolean stream expressions to convey violations to an operator, are defined using the keyword.

Stream expressions consist of common arithmetic and logical operators such as addition, subtraction, and conjunction. Higher-level mathematical functions such as sine, cosine, or the square root can be enabled by importing the module. To access past stream values at discrete positions RTLola includes the operator to access the n-th last value of a stream. As the n-th last value of a stream might not exist, as the stream, for example, only has n-1 positions yet, an offset operation must be followed by a default value to choose in that case.

Consider the following simplified specification of the scenario explained in Sect. 2. We limit ourselves to a single non-moving intruder instead of multiple moving intruders and assume a synchronous timing model, i.e., all streams are evaluated when the input streams receive new values.

Example 1

(A Static Intruder).

figure h

The two input streams lat and lon represent the measurements of the drone’s GPS coordinates. The constants below capture the static position of the non-moving intruder. The output stream distance keeps track of the Euclidian distance between the drone and the intruder. To achieve this, it retrieves the current values of the input streams and uses the formula to compute the distance. The type of the distance stream is automatically inferred and can be omitted, denoted transparently in the example. This inferred type follows from the floating point numbers given by the stream accesses and the underlying functions that operate on this type. The output stream closer captures the temporal property that the drone gets closer to the intruder. For that, its stream expression compares the last value of the distance stream, expressed by the -operator, with the current one. Given that the stream expression consists of a comparison, the type of the closer stream is inferred as a boolean automatically. Finally, a trigger stream defines the condition when the distance is too close. To make the trigger more precise, we require that the closer stream also evaluates to . Therefore, the trigger only activates if the drone moves towards the intruder.

3.1 Semantics

The semantics of an RTLola specification is defined as a relation between input and output streams. Intuitively, it compares every stream value at every timepoint with the computed value described by the stream expression. The following definition gives the semantics [8, 21] for a subset of RTLola to cover the general idea without focusing on concrete details.

Definition 1

(Simplified RTLola Semantics). Let \(\varphi \) be an RTLola specification with input stream variables \(i_1,...,i_m\) and output and trigger stream variables \(s_1,...,s_{n}\). Let \(\tau _1,...,\tau _m\) be streams of length N of input values. The tuple \(\langle \sigma _1,...,\sigma _{n} \rangle \) of streams of length N is called an evaluation model with respect to \(\tau _1,...,\tau _m\) iff for each equation in \(\varphi \) the following holds:

$$ \sigma _i(j) = val(e_i)(j) \quad \textit{for}\quad 0 \le j \le N $$

where \(e_i\) is the corresponding stream expression of \(s_i\) and val(e)(j) is for the expressions in our example defined as:

$$\begin{aligned} val(c)(j) &= c\\ val(i_t)(j) &= \tau _t(j)\\ val(s_t)(j) &= \sigma _t(j)\\ val(f(e_1,...,e_k))(j) &= f(val(e_1)(j),...,val(e_k)(j))\\ val(e.\textit{offset}(\textit{by: }i).\textit{defaults}(\textit{to: } d))(j) &= {\left\{ \begin{array}{ll} val(e)(j+i) & \textit{for } 0 \le j+i\\ val(d)(j) & \text {otherwise} \end{array}\right. } \end{aligned}$$

3.2 Evaluation Algorithm

In contrast to imperative programs, the order of the equations in a stream-based specification does not influence the order of the evaluation. They are generally evaluated simultaneously, yet accesses between streams imply dependencies between streams and therefore affect the order. We represent these dependencies in a graph-based representation called the dependency graph. An analysis of this graph then computes a correct order of the stream evaluation: Every topological order of the dependency graph represents a valid order to process the streams during a single evaluation cycle.

Definition 2

(Dependency Graph). Let \(\phi \) be an RTLola specification. The dependency graph of \(\phi \) is a directed weighted multi-graph \(G = \langle V, E \rangle \) with \(V=\{i_1,...,i_m,s_1,...,s_n\}\). An edge \(e=\langle s_i, s_k, w \rangle \) is in E iff the expression of \(s_i\) contains \(s_k.\textit{offset}(\textit{by: }w)\) as a sub-expression. Analogously, edges with weight 0 are added for non-offset accesses.

Example 2

(Dependency Graph). The following graph describes the dependency graph for the specification in Example 1:

figure k

Every node in the graph corresponds to a stream in the specification. For a better illustration, we mark input streams green, output streams blue, and trigger streams red. The input streams and do not have outgoing edges since input streams represent the input data and do not have a stream expression. The output stream accesses the current value of the and stream in the computation resulting in the 0-edge in the dependency graph. The stream accesses the current and last value of the -stream, resulting in a zero and an offset edge. Unlike input streams, trigger streams have no incoming edges since no stream expression can access these streams. In our example, the outgoing edges of the trigger are the accesses to the and stream.

Using this graph, we can compute different evaluation orders for this specification ensuring that the monitor accesses the intended stream values: The specification allows every order in which the -stream is evaluated after the inputs, the -stream after the -stream, and at the end of the evaluation the trigger stream.

For Experts: Static Analysis

This expert subsection presents two static analyses based on the dependency graphs that guarantee a safe evaluation of stream-based specifications. The first analysis guarantees the existence of a unique evaluation model, whereas the second analysis computes an upper bound of the required memory. The latter analysis can determine if the monitor can run in a resource-constrained environment before execution.

Well-Formed Specifications. With this analysis, we define a syntactic criterion to guarantee the existence of a unique evaluation model. In general, we cannot find a unique evaluation model, if we cannot determine a order of the stream evaluation. This problem corresponds to a cycle in the dependency graph. First, consider the following examples of syntactically valid specifications and their dependency graph illustrating the problem.

Example 3

(Ill-defined Specifications and their Dependency Graph).

figure x

According to the semantics, the specification on the left has no evaluation model, since we cannot find an assignment for and that satisfies both equations. Such specifications are not well-defined and should be rejected by the RTLola framework. In comparison, the specification on the right has multiple evaluation models as long as and are equal. However, for this specification, we again cannot compute a valid evaluation order. As a result, neither specification can be evaluated algorithmically.

Both specifications have the same graph with an edge from stream to stream and an edge in the other direction, resulting in a cycle. Because of this cycle, we cannot determine a valid evaluation order and should reject the specification. We can give a syntactic criterion for well-definedness called well-formedness based on the dependency graph of a specification [8]:

Definition 3

(Well-formedness). A specification is well-formed, iff for every cycle in its dependency graph, the accumulated edge weight of the cycle is not zero.

As described in the definition, we do not forbid every cyclic behavior. The next example shows a valid specification containing a cycle in the dependency graph. It sums all values of the input stream :

Example 4

(Valid Cycle).

figure af

Here, the uses the offset expression to access the past value of itself. This access results in a cycle in the dependency graph, but the sum of this cycle is negative. Intuitively, this is allowed since past values are already computed, and we can ignore these accesses when building the evaluation order.

Static Memory Bounds. Secondly, we can determine the amount of values that need to be kept in memory for every stream. Again, this analysis is based on the dependency graph and allows giving static memory bounds for specifications.

Definition 4

(Memory Bound). Let \(G = \langle V, E \rangle \) be the dependency graph of the specification \(\varphi \). For every stream s in \(\varphi \) its memory bound is determined as \(max(\{-w \mid \langle o', s, w \rangle \in E\})\).

Intuitively, the memory-bound of a stream is defined by the largest offset at which the stream is accessed. All values before that bound are not required for further computation and can be discarded. For the specification in Example 1 the memory bound of all streams is zero except for the distance stream. Given that the stream is accessed with an offset of \(-1\), the memory bound of this stream is one. The memory bound of the specification as a whole is then the sum over the memory bounds of all input and output streams.

4 Event-Based and Periodic Streams

This section lifts the simplification of a static intruder to a moving intruder. For this, consider the following naïve extension of Example 1, where the intruder position is given as additional input streams intruder_lat and intruder_lon.

Example 5

(A Synchronous Moving Intruder).

figure ah

This specification is correct in a synchronous setting, i.e. a setting where all input streams receive a new value simultaneously. Yet, in reality, the intruder and the drone are independent systems. Hence, the measurements of the intruder’s position might not be synchronous with the drone’s position measurements. In an asynchronous setting, input streams receive values independent of each other. For this, every output stream and trigger is only evaluated if the input streams they (transitively) depend on receive a new value at the same time. Consider the specification from Example 5 in a synchronous and asynchronous setting as illustrated by the trace on the right. First, all values are received synchronously and the stream is computed. It might seem correct, yet all output streams and trigger streams still transitively or directly depend on all input streams. As a result, the specification is effectively synchronous again and behaves invalidly if not all inputs receive a new value at the same time, as depicted later in the trace. As we can see, if the current position and the intruder position are not synchronized, the stream is not updated as expected.

The next example depicts the corrected specification, where input streams are accessed asynchronously using -accesses:

Example 6

(A Moving Intruder).

figure al

While the distance stream mathematically performs the same computation, a lookup is used to avoid a direct dependency. This lookup refers to the most current available value of the accessed stream and does not require that the accessed value is computed at the same time. However, there might not be such a value when the accessing stream is evaluated, so a default value must be supplied similar to the offset operator. As the timing of the distance stream is decoupled from any input stream, it has to be explicitly specified when it should produce new values. Syntactically, this is specified through a positive boolean expression over input streams following the after a stream’s name. This expression is called the activation condition of the stream and symbolically describes the events at which the stream is evaluated. In the example, the distance stream is evaluated whenever the intruders or the drone’s position changes. For the closer stream the activation condition is the same as for the distance, but is automatically inferred because of the synchronous access.

Moreover, the trigger in the specification has changed. It is now periodic, a concept which will be introduced in the subsequent section.

4.1 Periodic Streams

In reality, it is often required that a monitor not only reacts to system actions but can also proactively produce verdicts about the system’s health. Otherwise, the monitor could not detect a frozen system because it would freeze as well. In RTLola, proactive monitoring is achieved through streams evaluated at a fixed frequency called periodic streams. A periodic stream can be specified by giving a frequency or period annotation like or after the keyword. These frequencies are independent of input streams and to access input streams from periodic output streams we can either use -accesses or sliding windows. In our example, we use a sliding window in the trigger stream to make the specification more robust against GPS fluctuations.

Sliding window aggregations aggregate over every value in a given time frame of a stream using an aggregation function. More concretely, the window in our example evaluates only to true if every value in the last 5 s is true. The functionality of this sliding window is visualized in Fig. 4. In our specification, the trigger stream is evaluated with a fixed frequency of one second whereas the stream depends on the inputs. For the first four seconds, the window does not aggregate any values given that the whole duration is not available at the start of the monitoring. In this case, the window evaluates to the default value instead. Afterwards, the window aggregates over different numbers of closer values as illustrated by the different colors.

RTLola supports a set of aggregation functions as or .

Fig. 4.
figure 4

The functioning of sliding windows exemplified based on Example 6

For Experts: RTLola’s Type System

Similar to many programming languages, RTLola has a type system that ensures only valid specifications can be executed. The type-system of RTLola is twofold: The value type of a stream describes how the memory should be interpreted, i.e., as a signed or unsigned integer, a floating point number, or a string. The underlying value type system ensures that only compatible values are used in an operation, such that, for example, no string and number are added. The pacing type of a stream describes its timing behavior, i.e., it determines the points in time when the stream is evaluated. This pacing type system ensures that every direct access, also called synchronous access, is valid. Consider the following example specification with an invalid synchronous access:

Example 7

(Invalid Synchronous Access).

figure ax

The specification on the left has two output streams: The stream x, evaluated whenever the input a receives a new value, with that same value of a. The stream y is evaluated whenever the input b receives a new value and takes the value of x. The diagram on the right exemplifies the timing behavior of the streams. At time 1.2 and 1.8, only stream b receives a new value. Consequently, the stream x is not computed, and with that stream y cannot access the value of x at these points in time. These timing errors can lead to invalid memory accesses at runtime, so the type checker rejects the specification. To fix the specification, one could use a access from y to x similar to Example 6, resulting in the greyed-out arrows in the diagram on the right.

The Pacing Type System. Intuitively, the pacing type system declares a synchronous access valid if the accessed stream is evaluated at least at the same points in time as the accessing stream. For non-periodic, also called event-based streams, this property can be checked by asserting logical implication between the activation condition of the accessing stream and the accessed stream. In Example 7, it is easy to see that \(a \rightarrow b\) does not always hold, but, for example, \(a \wedge b \rightarrow a\) does. A similar condition holds for periodic streams: A synchronous access between periodic streams is valid if the accessing stream runs at a slower pace than the accessed stream. Concretely, the period of the accessed stream has to be a multiple of the period of the accessing stream. In addition, synchronous accesses between periodic and event-based streams are never valid.

5 Stream Lifecycle

In this section, we optimize the specification from Example 6 by guarding computationally heavy operations with cheap-to-evaluate predicates. Concretely, we only compute the distance and closer streams and the trigger when the intruder is in range. These optimizations are enabled by lifting the assumption that a stream exists from the start to the end of the monitor. Instead, we allow for dynamic stream creation, i.e., a stream can be created and removed from the monitor at runtime. In the following, we refer to the creation of streams as their spawn behavior and their deletion as their close behavior. In between its spawn and close action, a stream is evaluated as defined by its stream expression.

Syntactically, the three steps of a stream’s lifecycle are specified by three sub-clauses in the stream’s definition:

figure az

Each of these clauses can feature a pacing and a boolean stream expression preceded by the keyword. The pacing of a clause statically determines when the clause could be evaluated, i.e., whenever event occurs or at a frequency of . The stream expression preceded by is evaluated at the time points described by the pacing. It constrains these time points dynamically based on runtime values, i.e., the action corresponding to the clause is only taken when this expression evaluates to true. The evaluation clause features a stream expression defining how the stream’s value is computed after the keyword.

As for the previous sections, pacings can be omitted and inferred by the type checker for brevity. The expression of a clause can also be omitted, representing the constant . If a stream’s spawn or close clause is omitted, the stream exists from the start or until the end of the monitor, respectively. If only an clause exists, we can use the short-hand notation as used in the previous sections.

Consider the following extension of Example 6 in which every stream in the specification is now composed of three clauses: a spawn clause, an eval clause, and a close clause.

Example 8

(An Out-of-Range Intruder).

figure bj

Another notable change is the added stale stream. It defines when the intruder is considered out of range by checking for any GPS coordinate updates in the last 10 s. The clause of the stream defines that this condition is only monitored once a GPS location is received from the intruder. The omitted expression in the spawn clause is equivalent to a definition. The clause of the stream defines that the condition should no longer be monitored as soon as it becomes true for the first time. However, the stream is spawned again if a new intruder GPS location reactivates the spawn condition of the stream.

The distance and closer streams inherit the same and clauses as the stale stream, further excluding redundant computations. The trigger also inherits the same close condition as the other two streams. Yet, its spawn condition differs slightly. The expression is moved from the main trigger condition to its spawn condition, adapting the spawn activation condition accordingly. That way, the computationally heavy sliding window aggregation is only performed once the intruder is close enough.

Besides optimizing specifications, dynamic stream creations increase the expressiveness of the RTLola specification language as shown in the next subsection.

5.1 Deadline Watchdogs

Deadline watchdogs are common specification requirements in CPS and can be expressed in natural language as follows: “t seconds after event e a condition c must hold.” Such requirements can be represented in RTLola using dynamically created streams:

Example 9

(A Deadline Watchdog).

figure br

For the watchdog, we define a new stream that is spawned with the start of the watchdog, i.e. the event e spawns the watchdog. This is the starting point of the annotated frequency, so this stream is evaluated for the first time t seconds after e. Since this value is immediately true, we also close the stream after these t seconds. The trigger stream has the same and condition and is evaluated with the same frequency. Here, we check if the condition c is satisfied and use the stream as a helper function to immediately close the trigger stream after the first evaluation.

For Experts: Semantic Types

The introduced -conditions further refine the timing of a stream and add another point of failure for synchronous accesses. This problem is solved with another type system.

Semantic Types and Event-Based Streams. The following example illustrates a possible point of failure using synchronous accesses and -conditions:

Example 10

(Invalid Semantic Types).

figure by

As specified by the expression of the clause of x, it only produces a value when the input a is greater than 4. This might not coincide with as required for the evaluation of y. Therefore, similar to Example 7, there are points in time when y is evaluated, but x is not. To detect specifications with such errors, RTLola uses another type system reasoning about the when conditions, called semantic types in that context. Like the pacing type system, the semantic type system ensures the implication relation between semantic types holds. In the above example, the type system would reason whether the implication \( a > 3 \rightarrow a > 4\) is a tautology and consequently reject this specification.

Besides ensuring that the when conditions of the eval clauses of dependent streams imply each other, the semantic type system also reasons about the lifecycle of dependent streams. Concretely, for a synchronous access to succeed, the accessed stream must be alive at least as long as the accessing stream.

Semantic Types and Periodic Streams. While the previous intuition holds for event-based streams, synchronous accesses between periodic streams imply stricter requirements. Consider the following example:

Example 11

(Shifted Periodicity).

figure cc

Here the streams x and y have the same frequency and the spawn condition of y implies the spawn condition of x. However, the synchronous access might fail if x spawns before y since the frequencies are now out of sync. For example, the sequence of events depicted on the right of the example will lead to an invalid synchronous access. First, a has the value 1 and since no spawn condition is true both streams are not spawned. Next, a gets the value 4 spawning x but not y. The later stream is spawned half a second later, which results in the two periods of x and y not being synchronized. This leads to the failure of the synchronous access. To circumvent this problem, the semantic type system requires equality instead of the implication of the spawn and close condition of two dependent periodic streams.

Type System Decidability. Pacing types are defined as positive boolean formulas over input stream names or as fixed frequencies. Hence, it is efficiently decidable if their implication is a tautology. On the contrary, semantic types are arbitrary stream expressions. As described by Schwenger [21], whether an implication between stream expressions is a tautology is generally undecidable. Nevertheless, the RTLola frontend provides a sound over-approximating implementation of the semantic type checker based on syntactic equality. Here, semantic types are parsed as a boolean formula, so implications such as can still be proven.

6 Parameterization

The specifications in the previous sections were limited to a single intruder. However, in reality, the monitor must observe an unbounded amount of intruders since we can not give an apriori bound on their number. This monitor will inevitably require unbounded memory, yet keeping the memory footprint of the monitor predictable is essential for CPS. For this, RTLola features parameterized output streams [12] that provide a declarative and predictable way of handling unbounded memory through stream expressions.

Parameterized streams lift output streams from a single instance to a set of stream instances. While all stream instances share the stream expressions, each instance of a stream has a different assignment of parameter values. This assignment is determined by an additional stream expression preceded by the keyword in the clause. If an output stream is parameterized over multiple parameters, this expression returns a tuple of values matched position-wise to the parameters. Parameters are declared as a comma-separated sequence in braces after the stream name. The spawn clause of a parameterized stream determines when an instance is created as introduced in Sect. 5. The evaluation and close clauses of parameterized streams are evaluated for each instance, determining their value and lifecycle. As for dynamic streams, a stream instance will not be spawned again if an instance with the parameter values already exists.

Consider the final iteration of the running example which extends each output stream with a parameter for the different intruders:

Example 12

(Multiple Intruder).

figure cg

Notice the additional input stream intruder_id. We assume that every intruder has a unique ID provided to the monitor with every update of the intruder_lat and intruder_lon streams. The output stream intruder_pos is added to accumulate these positions on a per-intruder basis. It has a single parameter representing the intruder ID. This is made explicit through its expression that synchronously accesses the intruder_id stream. As a result, a new instance of this stream is created for each fresh intruder when it is received for the first time. All other output streams share the same parameterization, so each output stream has an instance for each non-stale intruder ID. The trigger features the additional condition as before. Finally, this specification achieves the goal outlined in Sect. 2 and handles multiple moving intruders efficiently and predictably.

For Experts: Instance Aggregations

Like sliding window aggregations, RTLola features instance aggregations to aggregate the most recent values of the instances of a parameterized output stream. For example, the trigger from Example 12 can be rewritten without parameters using an instance aggregation as follows:

figure cj

The approaching stream is similar to the trigger from Example 12. The non-parameterized trigger now aggregates over all instances of this new stream using a disjunction. Semantically, this means that whenever any instance of the helper stream evaluates to true, the instance aggregation in the trigger will also evaluate to true, causing the trigger to activate.

Instead of aggregating all stream instances, it is sometimes desirable to only aggregate the instances that produced a fresh value in this evaluation cycle. This is specified by stating in the aggregation. This will couple the pacing of the caller of the aggregation to the evaluation pacing of the target stream of the aggregation. Other instance aggregation functions in RTLola are , and .

7 Development and Integration

During the previous sections, the examples featured links to the RTLola playground, a web-based implementation of the RTLola frontend and interpreter that can analyse and execute specifications locally in your browser without requiring installation. A version of this tutorial is also available thereFootnote 1, such that the specifications and examples from this tutorial can easily be experimented with by pressing the Copy to Editor button below them and clicking Run. The specifications are tested against a trace simulated using the Microsoft Flight Simulator. A video of this trace is included in the overview chapter in the playground, and its raw data can be inspected by switching to the Trace tab on the right.

Nonetheless, in real applications, it is necessary to run the monitor either natively or incorporate them within existing applications. The RTLola interpreter provides solutions for both: A library to seamlessly integrate the monitor into Rust applications, along with a standalone command-line application.

7.1 RTLola CLI

The simplest way to run the RTLola interpreter locally is the command-line application . The installation of the application can be achieved using the cargo package manager:

figure co

The application offers two modes of execution:

  • Analyze In this mode, the RTLola frontend is employed to check the provided specification for correctness. This involves checking for syntax errors, ensuring well-definedness and detecting type-related errors.

  • Monitor This mode enables the execution of a monitor based on the provided specification. After checking the specification for correctness, the monitor can be run in an offline or online setting. In an offline setting, the monitor analyzes a prerecorded trace, while in an online setting, the data arrives in real-time.

In the offline setting, the interpreter monitors a prerecorded trace in CSV format such as the following:

figure cp

This example trace defines three events occurring at times 0 s, 1 s, and 2 s, respectively, and assigns new values to two input streams called and . Notably, the hashmark signifies that the corresponding input stream does not receive a new value at that particular time. Subsequently, we can run the to monitor the specification specification.lola on this trace:

figure cu

Here, the argument specifies the type of time format utilized in the “time” column of the CSV file. In the example, the timestamp is given as time in seconds relative to the start of the monitor. Upon executing, each event in the CSV file is forwarded to the monitor, with the resulting output printed to the standard output.

In the online mode, the interpreter retrieves the inputs from a buffer and utilizes the real-time timestamps of the events when they arrive. The following command starts the monitor in an online setting:

figure cw

Here, the application waits for new events on standard input and forwards them to the monitor as soon as they arrive.

We illustrated two instances of using the RTLola interpreter command line application. For a comprehensive list of available command-line arguments and options, you can consult the documentation by executing the following command:

figure cx

The RTLola interpreter has successfully been employed to monitor drones in cooperation with the German Aerospace Center (DLR) and the aircraft manufacturer Volocopter. We identified a set of different monitoring applications, as reported by Baumeister et al. [2], which we discuss in the expert section.

Fig. 5.
figure 5

Process to integrate the monitor

For Experts: Monitoring Applications

The monitor can provide valuable feedback during the development of new components in the aerospace domain. In this safety-critical domain, pre-defined standards ensure that the concept of operation, requirements, design, and implementation are coherent and include several validation steps. We identified different applications in which monitoring can be used during the development of new components following such standards but also during the operation of these components:

  1. 1.

    Debugging The monitor provides feedback to the developer of the component. During the execution, the monitor checks whether the component works as intended and collects statistical information. The developer writes the specification and has access to the internal state of the component.

  2. 2.

    Validation The monitor is used to validate the component externally. The specification is written independently and has only access to the inputs and outputs of the component and not to the internal state. This application is, among others, helpful in validating that components by external companies follow their requirements and can be trusted.

  3. 3.

    Pre-Post-Flight Analysis This application uses monitoring to check whether all necessary components are operational. The monitor runs pre-defined test cases and validates that no irregular behavior is detected. After the flight, the monitor computes more sophisticated information for better evaluation of the flight or runs new specifications on past flights.

  4. 4.

    In-Flight Analysis/Safe Integration The monitor provides feedback about the safety of the drone during its operation. It validates the correctness of individual components to ensure a safe flight and reports to the pilot if a property is violated.

All these applications require the integration of the monitor into the development process. Previously, we utilized the command-line application of the RTLola interpreter to execute the monitor. In the following example, we demonstrate how to integrate the interpreter using the Rust library.

Integration with the RTLola API. We assume that the monitor is running on the drone and receives the input data over internal communication. The output of the monitor is then sent over TCP to a ground station displaying the trigger messages of the monitor so a pilot can take over. Figure 5 illustrates two steps for this integration process. As shown in the figure, the setup consists of three components: The monitor, in the center of the figure, is automatically generated from the specification and does not require further integration. However, two interfaces must be implemented to handle the communication with the system and the operator. These implementations are specific to the setup: The Event Conversion receives the incoming sensor readings and transforms the data into an internal representation the monitor understands. The Verdict Conversion transforms the internal representation of the monitor’s output to messages accepted by the ground station.

After providing all the missing implementations, we need to configure the monitor and start the evaluation. This results in the following code, skipping the concrete configuration of the and the :

figure da

Event Conversion. The Event Conversion receives sensor values and transforms the readings into an internal representation the monitor uses. In our setup, we receive the sensor values over a UDP connection as a byte-stream and differentiate between two types of messages: The first type of message is sent by the GNSS sensor that computes the latitude and longitude of the drone. The second type of message contains the latitude and longitude of the intruders. The byte-stream needs to be parsed and converted to map the incoming data to the corresponding input streams. The implementation in our setup uses the simplified interfaces shown in Fig. 6. After providing a parser function, the implementation receives the byte stream and parses this stream to an event called . The implementation of the interface is provided automatically by the macros and . These macros generate code for a factory that maps, for example, the field in the struct to the input stream in the monitor.

Verdict Conversion. The Verdict Conversion transforms the internal representation of the monitor’s output into messages in a form expected by the ground station. In our setup, this conversion interprets trigger messages as bytes and sends these over TCP to the ground station. The interfaces for this setup are implemented generically, so no further steps are needed.

Fig. 6.
figure 6

Concrete Implementation of the Event Conversion

8 Conclusion

The running example from our tutorial has illustrated the expressiveness of the RTLola specification language, which makes RTLola well-suited for complex application domains like aerospace. The development of the specifications is facilitated by the comprehensive support of the tool framework. Since the same specification can be used in multiple different settings, a specification can be validated early in a test environment or on log data, long before the monitor is integrated into the aircraft; the automatic analysis of the specification furthermore ensures that the monitor operates correctly and reliably.

RTLola has been very successful in the aerospace domain (cf. [2, 4]). RTLola has also been used in other cyber-physical applications, including cars [6] and medical equipment [14], and in domains beyond CPS, such as networks [12]. The combination of the highly expressive RTLola specification language with the reliability obtained by static analysis and the resource efficiency of the monitoring framework is of great use in all these settings.