IoT example

This commit is contained in:
Tony Garnock-Jones 2016-05-02 17:13:25 -04:00
parent 4372df1b40
commit 21a53ba948
8 changed files with 356 additions and 0 deletions

View File

@ -76,3 +76,34 @@ directly, and one using the high-level Syndicate DSL.
- Low-level implementation
- [DEMO](textfield/)
- [Source code](textfield/index.js) in plain JavaScript
## IoT Demo
This is a model of a home automation system.
The idea is to alert a homeowner to the possibility they have left the
stove switched on beyond the time they intended to.
Components in the model include:
- a switch for the stove;
- an electric power meter, which monitors the power drawn by the
stove;
- a TV, which displays alerts to the user; and
- a remote control for the system, which can be used to switch off
the stove remotely.
When the stove is switched on, a timer is started, and if a certain
time goes by without the stove being switched off, an alert is shown
on the TV.
The example was inspired by a talk given in May 2016 at the
[PL Seminar at Northeastern University's College of Computer Science](http://prl.ccs.neu.edu/seminars.html)
by
[Charles Consel](http://phoenix.inria.fr/index.php/members/54-charles-consel)
about the
[DiaSuite](http://phoenix.inria.fr/research-projects/diasuite) system
that he and his collaborators have been developing.
- [DEMO](iot/)
- [Source code](iot/index.js) using the Syndicate/js DSL

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

View File

@ -0,0 +1,63 @@
<!doctype html>
<html>
<head>
<title>Syndicate: IoT Demo</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="style.css" rel="stylesheet">
<script src="../../third-party/jquery-2.2.0.min.js"></script>
<script src="../../dist/syndicatecompiler.js"></script>
<script src="../../dist/syndicate.js"></script>
<script type="text/syndicate-js" src="index.js"></script>
</head>
<body>
<h1>IoT Demo</h1>
<h2>Devices</h2>
<section>
<section>
<h3>TV</h3>
<div id="tv-container">
&nbsp;<div id="tv"></div>
</div>
</section>
<section>
<h3>Stove switch</h3>
<div id="stove-switch"></div>
<button id="stove-switch-on">Turn on switch</button>
<button id="stove-switch-off">Turn off switch</button>
</section>
<section>
<h3>Remote control</h3>
<button id="remote-control"><img src="img/remote.png"></button>
</section>
<section>
<h3>Power draw meter</h3>
<div id="power-draw-meter"></div>
</section>
</section>
<h2>Fault injection</h2>
<section>
<section>
<h3>Chaos Monkey</h3>
<div>
<button id="kill-power-draw-monitor">Kill power draw monitor</button>
<button id="kill-stove-switch">Kill stove-switch</button>
</div>
<div>
<button id="spawn-power-draw-monitor">Spawn power draw monitor</button>
<button id="spawn-stove-switch">Spawn stove-switch</button>
</div>
</section>
</section>
<hr>
<pre id="ds-state"></pre>
</body>
</html>

224
js/examples/iot/index.js Normal file
View File

@ -0,0 +1,224 @@
assertion type switchState(on);
assertion type powerDraw(watts);
assertion type time(now);
assertion type remoteClick();
assertion type tvAlert(text);
assertion type switchAction(on);
assertion type componentPresent(name);
assertion type DOM(containerSelector, fragmentClass, spec);
assertion type jQuery(selector, eventType, event);
///////////////////////////////////////////////////////////////////////////
// TV
function spawnTV() {
actor {
this.alerts = [];
this.alertFragment = ["ul"];
this.computeDisplay = function () {
var self = this; // omg javascript
this.alertFragment = ["ul"];
this.alerts.forEach(function (t) {
self.alertFragment.push(["li", t]);
});
};
forever {
assert DOM('#tv', 'alerts', Syndicate.seal(this.alertFragment));
on asserted tvAlert($text) {
this.alerts.push(text);
this.computeDisplay();
}
on retracted tvAlert($text) {
this.alerts = this.alerts.filter(function (t) { return t !== text; });
this.computeDisplay();
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Remote control and listener
function spawnRemoteControl() {
actor {
forever {
assert componentPresent('remote control');
on message jQuery('#remote-control', 'click', _) {
:: remoteClick();
}
}
}
}
function spawnRemoteListener() {
actor {
this.stoveIsOn = false;
// In principle, we should start up in "power undefined" state and
// count clicks we get in that state; when we then learn the real
// state, if we've been clicked, turn it off. We don't do this
// here, for simplicity.
forever {
on asserted powerDraw($watts) {
this.stoveIsOn = watts > 0;
}
on message remoteClick() {
if (this.stoveIsOn) {
:: switchAction(false);
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Stove switch and power draw monitor
function spawnStoveSwitch() {
actor {
this.powerOn = false;
state {
assert componentPresent('stove switch');
assert switchState(this.powerOn);
assert DOM('#stove-switch', 'switch-state',
Syndicate.seal(["img", [["src",
"img/stove-coil-element-" +
(this.powerOn ? "hot" : "cold") + ".jpg"]]]));
on message jQuery('#stove-switch-on', 'click', _) { this.powerOn = true; }
on message jQuery('#stove-switch-off', 'click', _) { this.powerOn = false; }
on message switchAction($newState) {
this.powerOn = newState;
}
} until {
case message jQuery('#kill-stove-switch', 'click', _);
}
}
}
function spawnPowerDrawMonitor() {
actor {
this.watts = 0;
state {
assert componentPresent('power draw monitor');
assert powerDraw(this.watts);
assert DOM('#power-draw-meter', 'power-draw',
Syndicate.seal(["p", "Power draw: ",
["span", [["class", "power-meter-display"]],
this.watts + " W"]]));
on asserted switchState($on) {
this.watts = on ? 1500 : 0;
}
} until {
case message jQuery('#kill-power-draw-monitor', 'click', _);
}
}
}
///////////////////////////////////////////////////////////////////////////
// Clock and "timeout listener"
function spawnClock() {
actor {
setInterval(Syndicate.Dataspace.wrap(function () {
:: time(+(new Date()));
}), 200);
forever {
assert componentPresent('real time clock');
}
}
}
function spawnTimeoutListener() {
var message = tvAlert('Stove on too long?');
actor {
this.mostRecentTime = 0;
this.powerOnTime = null;
forever {
on asserted powerDraw($watts) {
this.powerOnTime = (watts > 0) ? this.mostRecentTime : null;
}
on message time($now) {
this.mostRecentTime = now;
if (this.powerOnTime !== null && this.mostRecentTime - this.powerOnTime > 3000) {
Syndicate.Dataspace.stateChange(Syndicate.assert(message));
} else {
Syndicate.Dataspace.stateChange(Syndicate.retract(message));
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Failure monitor
function spawnFailureMonitor() {
function messageFor(who) {
return tvAlert('FAILURE: ' + who);
}
actor {
forever {
on asserted componentPresent($who) {
Syndicate.Dataspace.stateChange(Syndicate.retract(messageFor(who)));
}
on retracted componentPresent($who) {
Syndicate.Dataspace.stateChange(Syndicate.assert(messageFor(who)));
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Chaos Monkey
function spawnChaosMonkey() {
actor {
forever {
on message jQuery('#spawn-power-draw-monitor', 'click', _) {
spawnPowerDrawMonitor();
}
on message jQuery('#spawn-stove-switch', 'click', _) {
spawnStoveSwitch();
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// Main
$(document).ready(function () {
ground dataspace G {
Syndicate.JQuery.spawnJQueryDriver();
Syndicate.DOM.spawnDOMDriver();
spawnTV();
spawnRemoteControl();
spawnRemoteListener();
spawnStoveSwitch();
spawnPowerDrawMonitor();
spawnClock();
spawnTimeoutListener();
spawnFailureMonitor();
spawnChaosMonkey();
}
G.dataspace.onStateChange = function (mux, patch) {
$("#ds-state").text(Syndicate.prettyTrie(mux.routingTable));
};
});

38
js/examples/iot/style.css Normal file
View File

@ -0,0 +1,38 @@
#tv-container {
background: url('img/tvscreen.gif');
background-size: 100%;
background-repeat: no-repeat;
width: 400px;
height: 300px;
}
#tv-container .alerts {
color: white;
margin: 3em;
max-width: 11em;
}
.power-meter-display {
font-size: 24pt;
background-color: black;
color: red;
padding: 0.2em;
margin: 0.2em;
display: inline-block;
}
h2 {
background: lightgrey;
}
h3 {
border-bottom: solid grey 1px;
}
body > section {
display: flex;
}
body > section > section {
margin: 1em;
}