Skip to content

Commit

Permalink
Merge branch 'main' into cpp-examples
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardalee authored Feb 4, 2024
2 parents 9259c30 + 6788633 commit a481f0f
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 2 deletions.
5 changes: 3 additions & 2 deletions examples/C/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# C Examples
* [Patterns](src/patterns/README.md): Common communication patterns.
* [Deadlines](src/deadlines/README.md): Uses of deadlines in Lingua Franca.
* [Car Brake](src/car-brake/README.md): Sketch of ADAS system illustrating CAL theorem.
* [Car Brake](src/car-brake/README.md): Sketch of ADAS system illustrating the CAL theorem.
* [Rosace](src/rosace/README.md): Aircraft controller illustrating periodic systems with multiple periods.
* [Simulation](src/simulation/README.md): Using Lingua Franca for simulation.
* [Keyboard](src/keyboard/README.md): Responding to keyboard input using ncurses.
Expand All @@ -12,4 +12,5 @@
* [Shared Memory](src/shared-memory/README.md): Using shared memory to exchange large data objects between federates.
* [Train Door](src/train-door/README.md): Train door controller from a verification paper.
* [Distributed](src/distributed/README.md): Basic federated hello-world examples.
* [Leader Election](src/leader-election/README.md): Federated fault-tolerant system with leader election.
* [Watchdog](src/watchdog/README.md): Federated illustration of watchdogs.
* [Leader Election](src/leader-election/README.md): Federated fault-tolerant system with leader election.
27 changes: 27 additions & 0 deletions examples/C/src/watchdog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Watchdog -- Reacting to Absent Inputs

The LF deadline handlers react to inputs that are late.
What if you want to react the fact that an input has _not_ arrived by a certain time?
The deadline construct, by itself, will not do this.
It does not react until an input actually arrives.

The watchdog construct provides a mechanism for this.
A watchdog is started using `lf_watchdog_start` at some logical time _t_.
It will expire at _physical_ time _t_ + _W_, where _W_ is the watchdog delay,
unless it gets restarted with `lf_watchdog_start` or stopped using `lf_watchdog_stop`
before that physical time elapses.

When a watchdog expires, two things happen. First, the watchdog handler is invoked.
Second, an event identified by the name of the watchdog is scheduled at the earliest
available tag (typically the current tag plus one microstep).
The watchdog handler is invoked asynchronously, and therefore has limited access
to the reactor's infrastructure, such as inputs and outputs.
However, the scheduled watchdog event can trigger an ordinary reaction
which has full access to the state variables and inputs and outputs of the reactor.

<table>
<tr>
<td> <img src="img/Watchdog.png" alt="Watchdog" width="400">
<td> <a href="Watchdog.lf">Watchdog.lf</a>: Illustration of the use of the watchdogs in LF.</td>
</tr>
</table>
114 changes: 114 additions & 0 deletions examples/C/src/watchdog/Watchdog.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* @brief Demonstration of watchdogs in LF.
*
* This program has a periodic source that triggers events every 500 ms, but where every fourth
* event is delayed before being delivered to the downstream Watcher. This delay models either
* communication or computation time.
*
* The Watcher has a deadline of 200 ms. It wants to see events within 200 ms of their logical time,
* but, because of the delay every fourth event, the deadline will be violated. The deadline
* violation handler is invoked in this case, but it is only invoked after the event arrives, which
* is late. What if we need to react to the deadline violation sooner, e.g. in order to drive an
* actuator?
*
* To handle this, the Watcher includes watchdog named "dog" that triggers if any input event is
* more than 250 ms late.
*
* @author Benjamin Asch
* @author Edward A. Lee
*/
target C {
timeout: 5 s
}

reactor SometimesSlowSource {
output out: int
timer t(500 ms, 500 ms) // Offset ameliorates startup time.
state count: int = 1

reaction(t) -> out {=
if (self->count % 4 == 0) {
// Be slow.
lf_sleep(MSEC(300));
}
lf_set(out, self->count++);
=}
}

reactor Watcher {
input in: int
output out: int
state count: int = 1
logical action a(500 ms)

watchdog dog(750 ms) {=
instant_t p = lf_time_physical_elapsed();
lf_print("******** Watchdog timed out at elapsed physical time: " PRINTF_TIME, p);
=}

reaction(in) -> dog, out {=
// Reset the watchdog. The next event is expected in 500 ms.
// This watchdog will trigger if the event does not arrive within 750 ms from now,
// or is more than 250 ms late.
lf_watchdog_start(dog, 0);
lf_print("Watchdog started at physical time " PRINTF_TIME, lf_time_physical_elapsed());
lf_print("Will expire at " PRINTF_TIME, lf_time_logical_elapsed() + MSEC(750));
lf_set(out, in->value);
self->count++;
=} deadline(200 ms) {=
// This input is more than 200 ms late. It must have been from the slow cycles.
lf_print("Watcher received late input %d. Ignoring it.", in->value);
=}

reaction(dog) -> a {=
// Note that this reaction will trigger at the earliest available tag, which
// will be one microstep later than the most recent input or watchdog violation.
lf_print("******** Watchdog triggered. Scheduling output for next logical time.");
// Use a logical action so that the logical time of the output aligns.
lf_schedule(a, 0);
=}

reaction(a) -> out, dog {=
lf_print("******** Backup output being produced.");
lf_set(out, self->count++);
// Start another watchdog cycle in case the very next input is also delayed.
lf_watchdog_start(dog, 0);
lf_print("Watchdog started at physical time " PRINTF_TIME, lf_time_physical_elapsed());
lf_print("Will expire at " PRINTF_TIME, lf_time_logical_elapsed() + MSEC(750));
=}

reaction(shutdown) -> dog {=
lf_watchdog_stop(dog);
=}
}

reactor Checker {
input in: int
state count: int = 1

reaction(in) {=
lf_print("Checker received %d at logical time " PRINTF_TIME
" and physical time " PRINTF_TIME, in->value,
lf_time_logical_elapsed(),
lf_time_physical_elapsed());
if (in->value != self->count) {
lf_print_error_and_exit("Expected %d", self->count);
}
self->count++;
=}

reaction(shutdown) {=
if (self->count < 10) {
lf_print_error_and_exit("Received %d inputs. Expected at least 10.", self->count);
}
=}
}

federated reactor {
s = new SometimesSlowSource()
w = new Watcher()
c = new Checker()

s.out -> w.in
w.out -> c.in
}
Binary file added examples/C/src/watchdog/img/Watchdog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a481f0f

Please sign in to comment.