Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement R-Rosace using Watchdogs #113

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions examples/C/src/rosace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,16 @@ Note that these files have a difference [LICENSE](LICENSE.md) than the standard
<td> <img src="RosaceController.png" alt="Rosace controller" width="400">
<td> <a href="RosaceController.lf">RosaceController.lf</a>: A controller for an aircraft (from the ROSACE case study).</td>
</tr>
<tr>
<td> <img src="RedundantRosace.png" alt="Redundant Rosace controller" width="400">
<td> <a href="RedundantRosace.lf">RedundantRosace.lf</a>: A redundant version of the ROSACE controller.</td>
</tr>
<tr>
<td> <img src="RosaceWithUI.png" alt="Rosace controller with web interface" width="400">
<td> <a href="RosaceWithUI.lf">RosaceWithUI.lf</a>: A version of the ROSACE controller with a simple web interface.</td>
</tr>
<tr>
<td> <img src="RedundantRosaceWithWatchdog.png" alt="Rosace controller with web interface and watchdog" width="400">
<td> <a href="RedundantRosaceWithWatchdog.lf"> RedundantRosaceWithWatchdog.lf</a>: A version of the ROSACE controller with a web interface and a watchdog.</td>
</tr>
</table>
116 changes: 116 additions & 0 deletions examples/C/src/rosace/RedundantRosace.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* @brief This is a variant of the ROSACE case study with a redundant controller.
*
* It is drawn from the following paper:
*
* Deschamps, Henrick and Cappello, Gerlando and Cardoso, Janette and Siron, Pierre Coincidence
* Problem in CPS Simulations: the R-ROSACE Case Study. (2018) In: 9th European Congress Embedded
* Real Time Software and Systems ERTS2 2018, 31 January 2018 - 2 February 2018 (Toulouse, France).
* https://www.erts2018.org/authors_detail_inverted_Cappello%20Gerlando.html
*
* The Arbitrator in this program assumes it will receive logically simulatenous inputs from two
* controllers. If it receives any one input from a controller, it checks for the presence first of
* an input from the first controller, then the second controller. If it receives an input from both
* controllers, it will choose the input from the first controller.
*
* FailSilent reactors are inserted to emulate failures of the controllers. At a specified logical
* time, these simply drop the input.
*
* @author Edward A. Lee
*/
target C {
fast: true,
build: "./build_run_plot.sh RedundantRosace",
timeout: 10 min
}

import Aircraft from "AircraftSimulator.lf"
import RosaceController from "RosaceController.lf"
import PrintToFile from "../lib/PrintToFile.lf"
import Command from "Rosace.lf"

preamble {=
// Shared constants.
// Trimming parameters
#define Va_eq (230.0) // Nominal airspeed?
#define h_eq (10000.0)

#define delta_th_eq (1.5868660794926)
#define delta_e_eq (0.012009615652468)
=}

reactor Arbitrator {
input delta_thc1: double // Engine control
input delta_ec1: double // Elevator control
input delta_thc2: double // Engine control
input delta_ec2: double // Elevator control

output delta_thc: double // Engine control
output delta_ec: double // Elevator control

reaction(delta_thc1, delta_ec1, delta_thc2, delta_ec2) -> delta_thc, delta_ec {=
if (delta_thc1->is_present) {
lf_set(delta_thc, delta_thc1->value);
} else if (delta_thc2->is_present) {
// lf_print_warning("First controller delta_thc input is missing.");
lf_set(delta_thc, delta_thc2->value);
} else {
// lf_print_error("Both controllers' delta_thc inputs are missing!");
}
if (delta_ec1->is_present) {
lf_set(delta_ec, delta_ec1->value);
} else if (delta_ec2->is_present) {
lf_print_warning("First controller delta_ec input is missing.");
lf_set(delta_ec, delta_ec2->value);
} else {
lf_print_error("Both controllers' delta_ec inputs are failed!");
}
=}
}

reactor FailSilent(fail_time: time = 0) {
input in: double
output out: double

reaction(in) -> out {=
if (self->fail_time > 0 && lf_time_logical_elapsed() < self->fail_time) {
lf_set(out, in->value);
}
=}
}

main reactor(filter_period: time = 10 ms) {
a = new Aircraft()
c = new RosaceController(filter_period=filter_period)
altitude = new Command(value=11000) // Altitude command
speed = new Command(value=0.0) // Delta airspeed from nominal Va_eq (230)

p_h = new PrintToFile(filename="altitude.data")
p_Va = new PrintToFile(filename="airspeed.data")

a.h, a.az, a.Vz, a.q, a.Va -> c.h, c.az, c.Vz, c.q, c.Va
altitude.c -> c.c
speed.c -> c.s

c2 = new RosaceController(filter_period=filter_period) // Backup controller.
a.h, a.az, a.Vz, a.q, a.Va -> c2.h, c2.az, c2.Vz, c2.q, c2.Va
altitude.c -> c2.c
speed.c -> c2.s

f1 = new FailSilent(fail_time = 100 s)

ar = new Arbitrator()

c.delta_thc -> f1.in
f1.out -> ar.delta_thc1
c.delta_ec -> ar.delta_ec1

c2.delta_ec -> ar.delta_ec2
c2.delta_thc -> ar.delta_thc2

ar.delta_ec -> a.delta_ec
ar.delta_thc -> a.delta_thc

a.h -> p_h.y // Print connections.
a.Va -> p_Va.y
}
Binary file added examples/C/src/rosace/RedundantRosace.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions examples/C/src/rosace/RedundantRosaceWithWatchdog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head><title>Rosace Aircraft Controller</title></head>
<body>
<h1>Rosace Aircraft Controller</h1>
<p>
<b>Desired Altitude:</b>
<input type="text" id="altitude" value="10000" onchange="setAltitude(this.value)">
<input type="submit" value="Submit">
</p>
<p id="requested">No request.</p>
<p id="data">No data.</p>
<script>
function setAltitude(altitude) {
var xhttp = new XMLHttpRequest();
xhttp.onload = function() {
if (this.status == 200) {
document.getElementById('requested').innerHTML = "Requested altitude: " + this.responseText;
} else {
document.getElementById('requested').innerHTML = "Error: " + this.statusText;
}
}
xhttp.open('GET', '?altitude=' + altitude, true);
xhttp.send(null);
}
setInterval(function() {
var xhttp = new XMLHttpRequest();
xhttp.onload = function() {
if (this.status == 200) {
var parsed = JSON.parse(this.responseText);
document.getElementById('data').innerHTML
= "<b>Airspeed:</b> " + parsed.Va
+ "<br><b>Altitude:</b> " + parsed.h
+ "<br><b>Status:</b> " + parsed.status;
} else {
document.getElementById('data').innerHTML = "Error: " + this.statusText;
}
};
xhttp.open('GET', '/data', true);
xhttp.send(null);
}, 1000);
</script>
</body>
</html>
199 changes: 199 additions & 0 deletions examples/C/src/rosace/RedundantRosaceWithWatchdog.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/**
* ROSACE case study, from:
*
* Claire Pagetti , David Saussié, Romain Gratia , Eric Noulard , Pierre Siron, "The ROSACE Case
* Study: From Simulink Specification to Multi/Many-Core Execution," RTAS (2014).
*
* This implementation is based on code from:
*
* https://svn.onera.fr/schedmcore/branches/ROSACE_CaseStudy/.
*
* Since that original code bears an LGPL license, so does this program:
*
* (c) 2023 Regents of the University of California, LGPL-v3
* (https://www.gnu.org/licenses/lgpl-3.0.html)
*
* This program uses a forward Euler simulation of aircraft dynamics and implements throttle and
* elevator control. The parameters specified execute an elevation climb from an initial 10,000 feet
* to a target 11,000 feet.
*
* The style of execution is that each component has a `period` parameter that determines the
* frequency at which it runs. The program relies on persistence of inputs in the C target and on
* the dependency analysis of the *uses* field of reactor signature.
*
* To run this, it should be sufficient to just run `lfc` to compile it. The "build" parameter of
* the target provides a script that compiles the generated code, runs it, runs gnuplot to generate
* the plots, and then opens the resulting PDF files. If this script fails for any reason, comment
* the `build` attribute of the target declaration and do this by hand:
* ```
* cd examples-lingua-franca/C
* lfc src/rosace/Rosace.lf
* bin/Rosace
* gnuplot src/rosace/rosace.gnuplot
* open rosace.pdf
* ```

*
* You should see a smooth climb from 10,000 feet to 11,000 feet. You can experiment, for example,
* with the period with which the filters sample the sensor outputs from the aircraft model. If you
* change the `filter_period` parameter of the `RosaceController` from its default 10 ms to 100 ms,
* for example, you will far worse behavior from the aircraft.
*
* @author Edward A. Lee
* @author David Saussie
* @author Claire Pagetti
* @author Benjamin Asch
*/
target C {
keepalive: true,
timeout: 10 min,
coordination: decentralized
}

import Aircraft from "AircraftSimulator.lf"
import RosaceController from "RosaceController.lf"
import ServerUI from "../lib/ServerUI.lf"
import FailSilent from "RedundantRosace.lf"

preamble {=
// Shared constants.
// Trimming parameters
#define Va_eq (230.0) // Nominal airspeed?
#define h_eq (10000.0)

#define delta_th_eq (1.5868660794926)
#define delta_e_eq (0.012009615652468)
#define acceptable_delay (1000) // in msec

#include <stdlib.h> // Define strtol()
=}

reactor UserInterface(
period: time = 100 ms,
altitude_target: double = 10000,
// From nominal Va_eq (230)
airspeed_delta: double = 0.0) {
timer command(0, period) // Frequency with which to issue commands.
output altitude: double
output airspeed: double

input Va: double
input h: double
input status: int // 0: Primary, 1: Backup, 2: Panic

s = new ServerUI(hostport=8080, initial_file="RedundantRosaceWithWatchdog.html")

reaction(command) -> altitude, airspeed {=
lf_set(altitude, self->altitude_target);
lf_set(airspeed, self->airspeed_delta);
=}

reaction(s.request) Va, h, status -> s.response {=
char* response;
if(strncmp("/data", s.request->value, 5) == 0) {
// Construct JSON response.
asprintf(&response, "{\"Va\": %f, \"h\": %f, \"status\": %d}", Va->value, h->value, status->value);
} else if (strncmp("/?altitude=", s.request->value, 11) == 0) {
printf("-------- '%s'\n", s.request->value);
long result = strtol(s.request->value + 11, NULL, 10);
// Send back the request as a response.
asprintf(&response, "%ld", result);
self->altitude_target = (int)result;
} else {
printf("ERROR '%s'\n", s.request->value);
asprintf(&response, "Unrecognized request: %s", s.request->value);
}
lf_set_array(s.response, response, strlen(response) + 1);
=}
}

reactor Arbitrator(timeout: time = 250 ms) {
input delta_thc1: double // Engine control
input delta_ec1: double // Elevator control
input delta_thc2: double // Engine control
input delta_ec2: double // Elevator control

output delta_thc: double // Engine control
output delta_ec: double // Elevator control
output status: int // 0: Primary, 1: Backup, 2: Panic

watchdog watcher(timeout) {= lf_print_warning("Primary controller watchdog timeout."); =}

reaction(startup) -> status {=
lf_set(status, 0);
=}

initial mode Primary {
reaction(delta_thc1, delta_ec1) -> delta_thc, delta_ec, watcher {=
if (delta_thc1->is_present && delta_ec1->is_present) {
lf_set(delta_thc, delta_thc1->value);
lf_set(delta_ec, delta_ec1->value);
lf_watchdog_start(watcher, 0);
}
=}

reaction(watcher) -> reset(Backup), status, watcher {=
lf_print_warning("********* Switching to backup controller.");
lf_set(status, 1);
lf_watchdog_start(watcher, 0);
lf_set_mode(Backup);
=}
}

mode Backup {
reaction(delta_thc2, delta_ec2) -> delta_thc, delta_ec, watcher {=
if (delta_thc2->is_present && delta_ec2->is_present) {
lf_set(delta_thc, delta_thc2->value);
lf_set(delta_ec, delta_ec2->value);
lf_watchdog_start(watcher, 0);
}
=}

reaction(watcher) -> reset(Panic), status {=
lf_print_error("********* PANIC: Both controllers failed.");
lf_set(status, 2);
lf_set_mode(Panic);
=}
}

mode Panic {
timer t(0, 5 ms)
reaction(t) -> delta_thc, delta_ec {=
// No controller is present. Set failsafe outputs.
lf_set(delta_thc, 0.0);
lf_set(delta_ec, 0.0);
=}
}
}

main reactor(filter_period: time = 10 ms) {
a = new Aircraft()
c = new RosaceController(filter_period=filter_period)
bc = new RosaceController(filter_period=filter_period)
ui = new UserInterface()
ar = new Arbitrator()
f = new FailSilent(fail_time = 5 s)
f2 = new FailSilent(fail_time = 20 s)

a.h, a.az, a.Vz, a.q, a.Va -> c.h, c.az, c.Vz, c.q, c.Va
ui.altitude -> c.c
ui.airspeed -> c.s

a.h, a.az, a.Vz, a.q, a.Va -> bc.h, bc.az, bc.Vz, bc.q, bc.Va
ui.altitude -> bc.c
ui.airspeed -> bc.s

c.delta_ec -> ar.delta_ec1
c.delta_thc -> f.in
f.out -> ar.delta_thc1

bc.delta_ec -> ar.delta_ec2
bc.delta_thc -> f2.in
f2.out -> ar.delta_thc2

ar.delta_ec, ar.delta_thc -> a.delta_ec, a.delta_thc
ar.status -> ui.status

a.h -> ui.h // Print connections.
a.Va -> ui.Va
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/C/src/rosace/RosaceWithUI.lf
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ reactor UserInterface(
input Va: double
input h: double

s = new ServerUI()
s = new ServerUI(hostport=8080)

reaction(command) -> altitude, airspeed {=
lf_set(altitude, self->altitude_target);
Expand Down
Binary file added examples/C/src/rosace/RosaceWithUI.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions examples/C/src/watchdog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ unless it gets restarted with `lf_watchdog_start` or stopped using `lf_watchdog_
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).
Second, an event identified by the name of the watchdog is scheduled to execute at the logical time of the watchdog expiration.
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
Expand Down
Loading
Loading