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

Tutorial solutions #7

Open
wants to merge 34 commits into
base: solutions
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
18c997d
multiple agents
Crista2019 Jul 7, 2020
6be2651
clarify instructions to add agents
Crista2019 Jul 7, 2020
a588c8c
add exercise instructions to read.me
Crista2019 Jul 7, 2020
8ffe6c3
make exercise into actionable with TODO
Crista2019 Jul 8, 2020
1a268a1
add second try it yourself about stats lane
Crista2019 Jul 8, 2020
237a080
revise comment wording so I know what to separate out in the solution…
Crista2019 Jul 8, 2020
41748f2
empty template with TODOs and hints (w/o solutions)
Crista2019 Jul 8, 2020
93730e4
calculate basic average with stats value lane
Crista2019 Jul 8, 2020
d6bd22c
added second stats metric (local mean)
Crista2019 Jul 8, 2020
96a3716
add variance and std deviation calculations
Crista2019 Jul 8, 2020
15a584b
include more descriptive hints for logic to update stats
Crista2019 Jul 8, 2020
2515c3d
made tutorial vs solutions code clearer
Crista2019 Jul 8, 2020
85fb3ab
remove TODOs/instructions
Crista2019 Jul 8, 2020
a94cc0e
add suggestions to change UI after server exercises
Crista2019 Jul 9, 2020
dad986e
add suggestions to change UI after server exercises
Crista2019 Jul 9, 2020
789da1d
select web agent by dropdown with color changes
Crista2019 Jul 10, 2020
aece681
change color gradients for web agents, dropdown select
Crista2019 Jul 10, 2020
a2f852d
drop down to select web agent, color scheme change updates per agent
Crista2019 Jul 10, 2020
65290c2
differentiate data generated and sent to each web agent
Crista2019 Jul 10, 2020
d8901e3
send all stats data to one value lane of type Value
Crista2019 Jul 10, 2020
ea806bb
model the stats lane vals in the gauge UI
Crista2019 Jul 11, 2020
7c2a3e4
made TODO comment to add remove logic for histogram
Crista2019 Jul 11, 2020
3d5ee2f
variable naming convention refactor
Crista2019 Jul 13, 2020
3a639dd
commit with didRemove logic stopping point, before realizing that upa…
Crista2019 Jul 14, 2020
4a2ed02
add didRemove for avg and stats (local_* lanes may not require this l…
Crista2019 Jul 14, 2020
cfd7a9e
fixing scope/syntax errors in didRemove
Crista2019 Jul 14, 2020
698515b
fix casing for local final variables (from UPPER to camelCase)
Crista2019 Jul 14, 2020
f61a70e
update case of value labels to reflect last commit
Crista2019 Jul 14, 2020
794d58c
get item slot in histogram using 'count' key instead of index
Crista2019 Jul 14, 2020
f071c99
explain code solutions in the readmes
Crista2019 Jul 14, 2020
eea3c4f
flipping the () and [] markdown for links
Crista2019 Jul 14, 2020
fb541c1
fix variable name -> updatedStats
Crista2019 Jul 14, 2020
a89facf
remove gratuitous TODO
Crista2019 Jul 14, 2020
95a5f3c
make ++ and -- uniform without spaces before
Crista2019 Jul 15, 2020
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
14 changes: 14 additions & 0 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Swim implements a general purpose distributed object model. The "objects" in thi

[Creating a class](http://github.com/swimos/tutorial/tree/master/server/src/main/java/swim/tutorial/UnitAgent.java#L13) that extends `swim.api.agent.AbstractAgent` defines a *template* for Web Agents (though not a useful one until we add some [lanes](#lanes)).

#### *Example Solutions*
- *Created two additional web agents in (DataSource)[https://github.com/swimos/tutorial/blob/solutions/server/src/main/java/swim/tutorial/DataSource.java]*
Crista2019 marked this conversation as resolved.
Show resolved Hide resolved
- *Created unique Records (msg2 and msg3) to vary the data sent to different agents*

Visit the [documentation](https://developer.swim.ai/concepts/agents/) for further details about Web Agents.

## Lanes
Expand All @@ -20,6 +24,12 @@ Continuing our analogy, *lane callback* functions serve as the "methods" of Web

Each lane type defines a set of overridable (default no-op) lifecycle callbacks. For example, [sending a command message](#sending-data-do-swim) to any command lane will trigger its [`onCommand` callback](http://github.com/swimos/tutorial/tree/master/server/src/main/java/swim/tutorial/UnitAgent.java#L51-L54). On the other hand, [setting a value lane](http://github.com/swimos/tutorial/tree/master/server/src/main/java/swim/tutorial/UnitAgent.java#L53) will trigger its `willSet` callback, then update its value, then trigger its [`didSet` callback](http://github.com/swimos/tutorial/tree/master/server/src/main/java/swim/tutorial/UnitAgent.java#L40-L47).

#### *Example Solutions*
- *Added 5 new SwimLanes in (UnitAgent)[https://github.com/swimos/tutorial/blob/solutions/server/src/main/java/swim/tutorial/UnitAgent.java]*
Crista2019 marked this conversation as resolved.
Show resolved Hide resolved
- *the 'avg' ValueLane keeps track of changes to the mean cumulatively*
- *the 'localAvg', 'localVar', and 'localStdDev' ValueLanes run calculations on the 5 most recent data points sent from histogram*
- *the 'stats' ValueLane is an alternative design choice which tracks each of the above metrics using one lane of type Value (rather than 4 individual lanes of type Long)*

Visit the [documentation](https://developer.swim.ai/concepts/lanes/) for further details about lanes.

## Standing a Swim Server
Expand All @@ -43,3 +53,7 @@ Visit the [documentation](https://developer.swim.ai/concepts) for further detail
Swim client instances use Swim **links** to pull data from a Swim lanes. Like their corresponding lanes, links have overridable callback functions that can be used to [populate UIs](http://github.com/swimos/tutorial/tree/master/ui/index.html#L116-L141).

Visit the [documentation](https://developer.swim.ai/concepts/links/) for further details about links.

## *Visualizing Your Changes in the UI*

- *See the [**solutions**](https://github.com/swimos/tutorial/tree/solutions/ui) branch for an example of changes you could make to the UI to reflect these possible solutions*
20 changes: 20 additions & 0 deletions server/src/main/java/swim/tutorial/DataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ void sendCommands() throws InterruptedException {
// *Web Agent* addressable by "/unit/master" RUNNING ON the
// *(Swim) server* addressable by hostUri
this.ref.command(this.hostUri, "/unit/master", "publish", msg);

// *********************** EXAMPLE SOLUTION ***********************


// change and round scale of foo, bar, baz to make data sent to different agents more distinct and recognizable from each other
final Record msg2 = Record.create(3)
.slot("foo", (double)Math.round((foo + 20) * .5))
.slot("bar", (double)Math.round((bar - 25) * 1.05))
.slot("baz", (double)Math.round(baz * .5));

final Record msg3 = Record.create(3)
.slot("foo", (double)Math.round((foo + 5) * .5))
.slot("bar", (double)Math.round((bar + 5) * .75))
.slot("baz", (double)Math.round((baz + 10) * .15));

this.ref.command(this.hostUri, "/unit/secondAgent", "publish", msg2);
this.ref.command(this.hostUri, "/unit/thirdAgent", "publish", msg3);

// ****************************************************************

indicator = (indicator + 1) % 1000;

// Throttle events to four every three seconds
Expand Down
117 changes: 109 additions & 8 deletions server/src/main/java/swim/tutorial/UnitAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,115 @@
import java.util.Iterator;

public class UnitAgent extends AbstractAgent {

@SwimLane("histogram")
private final MapLane<Long, Value> histogram = this.<Long, Value>mapLane()
.didUpdate((k, n, o) -> {
logMessage("histogram: replaced " + k + "'s value to " + Recon.toString(n) + " from " + Recon.toString(o));
dropOldData();
});


// *********************** EXAMPLE SOLUTIONS FOR STATS LANE ***********************

// instance variables to track metrics going into stats
private long countSum = 0;
private int countTotal = 0;
private int index = 0;
private long[] recentData = new long[5];


// intermediary lanes that represent individual metrics
@SwimLane("avg")
private final ValueLane<Long> avg = this.<Long>valueLane()
.didSet((n, o) -> {
logMessage("avg: mean updated to " + n + " from " + o);
});

@SwimLane("localAvg")
private final ValueLane<Long> localAvg = this.<Long>valueLane()
.didSet((n, o) -> {
logMessage("localAvg: local average (last 5 entries) updated to " + n + " from " + o);
});

@SwimLane("localVar")
private final ValueLane<Long> localVar = this.<Long>valueLane()
.didSet((n, o) -> {
logMessage("localVar: local variance (last 5 entries) updated to " + n + " from " + o);
});

@SwimLane("localStdDev")
private final ValueLane<Long> localStdDev = this.<Long>valueLane()
.didSet((n, o) -> {
logMessage("localStdDev: local std deviation (last 5 entries) updated to " + n + " from " + o);
});


// combination all calculations into one swim lane of type Value
@SwimLane("stats")
private final ValueLane<Value> stats = this.<Value>valueLane()
.didSet((n, o) -> {
logMessage("stats: set to " + Recon.toString(n) + " from " + Recon.toString(o));
});

// *********************** EXAMPLE SOLUTION FOR HISTOGRAM ***********************
@SwimLane("histogram")
private final MapLane<Long, Value> histogram = this.<Long, Value>mapLane()
.didUpdate((k, n, o) -> {
logMessage("histogram: replaced " + k + "'s value to " + Recon.toString(n) + " from " + Recon.toString(o));

// calculating overall mean to send to average lane
countSum += n.get("count").longValue();
countTotal ++;
final long setAvg = countSum / countTotal;
avg.set(setAvg);

// appending new data to the recentData array
if (index >= recentData.length-1) {
index = 0;
}
recentData[index] = n.get("count").longValue();
index ++;
Crista2019 marked this conversation as resolved.
Show resolved Hide resolved

// calculating local mean to send to local average lane
long localSum = 0;
for (long d : recentData) localSum += d;
final long setLocalAvg = localSum / (long) recentData.length;
localAvg.set(setLocalAvg);

// calculating local variance to send to local var lane
long squaredDifSum = 0; // (sum of local mean - each value)^2
for (long d : recentData) squaredDifSum += (d - setLocalAvg)*(d - setLocalAvg);
final long setLocalVar = squaredDifSum/recentData.length;
localVar.set(setLocalVar);

// calculating local standard deviation to send to local standard deviation lane
final long setLocalStdDev = (long)Math.sqrt(setLocalVar);
localStdDev.set(setLocalStdDev);

// Consolidating all data to the valuelane stats of type value
Value all_stats = Record.create(4).slot("avg", setAvg).slot("localAvg", setLocalAvg).slot("localVar", setLocalVar).slot("localStdDev", setLocalStdDev);
stats.set(all_stats);

dropOldData();
})
.didRemove((k,o) -> {
// remove logic typically follows this format:
// stats.put(stats.get()-o)

logMessage("histogram: removed <" + k + "," + Recon.toString(o) + ">");

// remove logic for avg lane
countSum -= o.get("count").longValue() ;
countTotal --;
Crista2019 marked this conversation as resolved.
Show resolved Hide resolved
final long setUpdatedAvg = countSum / countTotal;
avg.set(setUpdatedAvg);

// stats based only on the most recent inputs (i.e. localAvg, et al) will constantly update already
final long setLocalAvg = localAvg.get();
final long setLocalVar = localVar.get();
final long setLocalStdDev = localStdDev.get();

// remove logic for stats
Value updated_stats = Record.create(4).slot("avg", setUpdatedAvg).slot("localAvg", setLocalAvg).slot("localVar", setLocalVar).slot("localStdDev", setLocalStdDev);
stats.set(updated_stats);
Crista2019 marked this conversation as resolved.
Show resolved Hide resolved

});

// ****************************************************************************

@SwimLane("history")
private final ListLane<Value> history = this.<Value>listLane()
.didUpdate((idx, newValue, oldValue) -> {
Expand Down
6 changes: 6 additions & 0 deletions ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

The minimum you need to visualize Swim data is a Swim client. That said, Swim comes with additional tools that let you see your data right away with no extra work.

#### *Example Solutions*

- [ ] *Use the dropdown menu to toggle between three different web agents for each UI demo*
- [ ] *See how each UI demo updates its downlinks when new web agents are selected*
- [ ] *Explore how to include simple visual changes (e.g. color changes for different agents) using the* [*swim UI framework*](https://docs.swimos.org/js/latest/index.html)

Read [chart.html](http://github.com/swimos/tutorial/tree/master/ui/chart.html) to build your own line chart.

Read [gauge.html](http://github.com/swimos/tutorial/tree/master/ui/gauge.html) to build your own gauge.
Expand Down
146 changes: 103 additions & 43 deletions ui/chart.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,114 @@
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, shrink-to-fit=no, viewport-fit=cover" />
</head>
<body style="display: flex; justify-content: center; align-items: center; width: 100vw; height: 100vh; margin: 0;">

<div id="app" style="display: flex; width: 67%; height: 67%; flex-direction: column;">
</div>

<div>
<label for="agents">Choose a web agent:</label>

<select name="agents" id="web_agent_select" onchange="update(this.value)">
<option value="/unit/master" style = "color:#50e3c2">/unit/master</option>
<option value="/unit/secondAgent" style = "color:#3c52f0">/unit/secondAgent</option>
<option value="/unit/thirdAgent" style = "color:#212326">/unit/thirdAgent</option>
</select>
</div>

<script src="https://cdn.swimos.org/js/3.10.2/swim-system.js"></script>
<script>

const app = new swim.HtmlAppView(document.getElementById("app"));

const chartCanvas = app.append('div').position('relative').append("canvas");

const tween = swim.Transition.duration(1000);

/* Chart View */
const chart = new swim.ChartView()
.bottomAxis(swim.AxisView.bottom("time"))
.leftAxis(swim.AxisView.left("0...120"))
.domainColor("#4a4a4a")
.tickMarkColor("#4a4a4a")
.font("12px sans-serif")
.textColor("#4a4a4a");
chartCanvas.append(chart);

const plot = new swim.LineGraphView()
.stroke("#50e3c2")
.strokeWidth(2);
chart.addPlot(plot);

function addToPlot(key, value) {
const time = key.numberValue();
const v = value.get("count").numberValue(0);
plot.insertDatum({x: time, y: v, opacity: void 0});
}

function removeFromPlot(key) {
const time = key.numberValue();
plot.removeDatum(time);
}

const histogramLink = swim.downlinkMap()
.hostUri("warp://localhost:9001")
.nodeUri("/unit/master")
.laneUri("histogram")
.didUpdate(function(key, value) {
addToPlot(key, value);
})
.didRemove(function(key) {
removeFromPlot(key);
})
.open();
const app = new swim.HtmlAppView(document.getElementById("app"));

const chartCanvas = app.append('div').position('relative').append("canvas");

const tween = swim.Transition.duration(1000);

/* Chart View */
const chart = new swim.ChartView()
.bottomAxis(swim.AxisView.bottom("time"))
.leftAxis(swim.AxisView.left("0...120"))
.domainColor("#4a4a4a")
.tickMarkColor("#4a4a4a")
.font("12px sans-serif")
.textColor("#4a4a4a");
chartCanvas.append(chart);

const plot = new swim.LineGraphView()
.stroke("#50e3c2")
.strokeWidth(2);
chart.addPlot(plot);

function addToPlot(key, value) {
const time = key.numberValue();
const v = value.get("count").numberValue(0);
plot.insertDatum({x: time, y: v, opacity: void 0});
}

function removeFromPlot(key) {
const time = key.numberValue();
plot.removeDatum(time);
}

// Allowing for Web Agent selection by html dropdown menu

var agent_URI = "/unit/master";

// runs once
var histogramLink = swim.downlinkMap()
.hostUri("warp://localhost:9001")
.nodeUri(agent_URI)
.laneUri("histogram")
.didUpdate(function(key, value) {
addToPlot(key, value);
})
.didRemove(function(key) {
removeFromPlot(key);
})
.open();


// update logic runs whenever a new dropdown option is selected
function update(val) {
if (histogramLink) {
// close downlink on update
histogramLink.close();

// update the node URI used in histogramLink to match the selected agent
agent_URI = document.getElementById('web_agent_select').value;
console.log(agent_URI);

histogramLink = swim.downlinkMap()
.hostUri("warp://localhost:9001")
.nodeUri(agent_URI)
.laneUri("histogram")
.didUpdate(function(key, value) {
addToPlot(key, value);
})
.didRemove(function(key) {
removeFromPlot(key);
})
.open();

// switch statement that changes the plot color according to web agent
var color = "#50e3c2";
switch(agent_URI) {
case "/unit/secondAgent":
color = "#3c52f0";
break;
case "/unit/thirdAgent":
color = "#212326";
break;
default:
// for master web agent
color = "#50e3c2";
}
plot
.stroke(color);
chart.addPlot(plot);
}
}

</script>
</body>
</html>
Loading