diff --git a/server/README.md b/server/README.md index 8a81b54..1eea4a6 100644 --- a/server/README.md +++ b/server/README.md @@ -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)* +- *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 @@ -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)* + - *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 @@ -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* diff --git a/server/src/main/java/swim/tutorial/DataSource.java b/server/src/main/java/swim/tutorial/DataSource.java index 1f23965..f4d639c 100644 --- a/server/src/main/java/swim/tutorial/DataSource.java +++ b/server/src/main/java/swim/tutorial/DataSource.java @@ -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 diff --git a/server/src/main/java/swim/tutorial/UnitAgent.java b/server/src/main/java/swim/tutorial/UnitAgent.java index faed0a5..d2e9a07 100644 --- a/server/src/main/java/swim/tutorial/UnitAgent.java +++ b/server/src/main/java/swim/tutorial/UnitAgent.java @@ -12,14 +12,115 @@ import java.util.Iterator; public class UnitAgent extends AbstractAgent { - - @SwimLane("histogram") - private final MapLane histogram = this.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 avg = this.valueLane() + .didSet((n, o) -> { + logMessage("avg: mean updated to " + n + " from " + o); + }); + + @SwimLane("localAvg") + private final ValueLane localAvg = this.valueLane() + .didSet((n, o) -> { + logMessage("localAvg: local average (last 5 entries) updated to " + n + " from " + o); + }); + + @SwimLane("localVar") + private final ValueLane localVar = this.valueLane() + .didSet((n, o) -> { + logMessage("localVar: local variance (last 5 entries) updated to " + n + " from " + o); + }); + + @SwimLane("localStdDev") + private final ValueLane localStdDev = this.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 stats = this.valueLane() + .didSet((n, o) -> { + logMessage("stats: set to " + Recon.toString(n) + " from " + Recon.toString(o)); + }); + + // *********************** EXAMPLE SOLUTION FOR HISTOGRAM *********************** + @SwimLane("histogram") + private final MapLane histogram = this.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++; + + // 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--; + 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(updatedStats); + + }); + + // **************************************************************************** + @SwimLane("history") private final ListLane history = this.listLane() .didUpdate((idx, newValue, oldValue) -> { diff --git a/ui/README.md b/ui/README.md index 6821f30..70f5bb0 100644 --- a/ui/README.md +++ b/ui/README.md @@ -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. diff --git a/ui/chart.html b/ui/chart.html index 0ae92c6..3f8a2dc 100644 --- a/ui/chart.html +++ b/ui/chart.html @@ -5,54 +5,114 @@ +
+ +
+ + + +
+ diff --git a/ui/gauge.html b/ui/gauge.html index a4d18bd..5d4c57c 100644 --- a/ui/gauge.html +++ b/ui/gauge.html @@ -7,6 +7,17 @@
+ +
+ + + +
+ diff --git a/ui/pie.html b/ui/pie.html index 479a235..54f77e2 100644 --- a/ui/pie.html +++ b/ui/pie.html @@ -7,6 +7,17 @@
+ +
+ + + +
+