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 @@
+ +
+ + + +
+ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy