diff --git a/examples/C/README.md b/examples/C/README.md index fbc1fab4..d28c1f47 100644 --- a/examples/C/README.md +++ b/examples/C/README.md @@ -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. @@ -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. \ No newline at end of file +* [Watchdog](src/watchdog/README.md): Federated illustration of watchdogs. +* [Leader Election](src/leader-election/README.md): Federated fault-tolerant system with leader election. diff --git a/examples/C/src/watchdog/README.md b/examples/C/src/watchdog/README.md new file mode 100644 index 00000000..c788c153 --- /dev/null +++ b/examples/C/src/watchdog/README.md @@ -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. + + + + + +
Watchdog + Watchdog.lf: Illustration of the use of the watchdogs in LF.
\ No newline at end of file diff --git a/examples/C/src/watchdog/Watchdog.lf b/examples/C/src/watchdog/Watchdog.lf new file mode 100644 index 00000000..8d0db5ef --- /dev/null +++ b/examples/C/src/watchdog/Watchdog.lf @@ -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 +} diff --git a/examples/C/src/watchdog/img/Watchdog.png b/examples/C/src/watchdog/img/Watchdog.png new file mode 100644 index 00000000..3bb720f5 Binary files /dev/null and b/examples/C/src/watchdog/img/Watchdog.png differ