blob: 18d729d22cefded438932e1333e50eedc4133526 [file] [log] [blame]
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keywords" content=" ">
<title>Application console | Apache Edgent Documentation</title>
<link rel="stylesheet" type="text/css" href="../css/syntax.css">
<link rel="stylesheet" type="text/css" href="../css/font-awesome.min.css">
<!--<link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css">-->
<link rel="stylesheet" type="text/css" href="../css/modern-business.css">
<link rel="stylesheet" type="text/css" href="../css/lavish-bootstrap.css">
<link rel="stylesheet" type="text/css" href="../css/customstyles.css">
<link rel="stylesheet" type="text/css" href="../css/theme-blue.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<script src="../js/jquery.navgoco.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/2.0.0/anchor.min.js"></script>
<script src="../js/toc.js"></script>
<script src="../js/customscripts.js"></script>
<link rel="shortcut icon" href="../common_images/favicon.ico" type="image/x-icon">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container topnavlinks">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="fa fa-home fa-lg navbar-brand" href="../docs/home.html">&nbsp;<span class="projectTitle"> Apache Edgent Documentation</span></a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<!-- entries without drop-downs appear here -->
<!-- conditional logic to control which topnav appears for the audience defined in the configuration file.-->
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">GitHub Repos<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="https://github.com/apache/incubator-edgent" target="_blank">Source code</a></li>
<li><a href="https://github.com/apache/incubator-edgent-samples" target="_blank">Edgent Samples</a></li>
<li><a href="https://github.com/apache/incubator-edgent-website" target="_blank">Website/Documentation</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Javadoc<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="..\javadoc\latest">latest</a></li>
<li><a href="..\javadoc\r1.2.0">1.2.0</a></li>
<li><a href="..\javadoc\r1.1.0">1.1.0</a></li>
<li><a href="..\javadoc\r1.0.0">1.0.0</a></li>
<li><a href="..\javadoc\r0.4.0">0.4.0</a></li>
</ul>
</li>
<!-- entries with drop-downs appear here -->
<!-- conditional logic to control which topnav appears for the audience defined in the configuration file.-->
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Edgent Resources<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="downloads">Download</a></li>
<li><a href="faq">FAQ</a></li>
<li class="dropdownActive"><a href="/">edgent.apache.org</a></li>
</ul>
</li>
<!-- special insertion -->
<!-- Send feedback function -->
<script>
function SendLinkByMail(href) {
var subject= "Apache Edgent Documentation feedback";
var body = "I have some feedback about the Application console page: ";
body += window.location.href;
body += "";
var uri = "mailto:?subject=";
uri += encodeURIComponent(subject);
uri += "&body=";
uri += encodeURIComponent(body);
window.location.href = uri;
}
</script>
<li><a href="mailto:dev@edgent.incubator.apache.org" target="_blank"><i class="fa fa-envelope-o"></i> Feedback</a></li>
<!--uncomment this block if you want simple search instead of algolia-->
<li>
<!--start search-->
<div id="search-demo-container">
<input type="text" id="search-input" placeholder="search...">
<ul id="results-container"></ul>
</div>
<script src="../js/jekyll-search.js" type="text/javascript"></script>
<script type="text/javascript">
SimpleJekyllSearch.init({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
dataSource: '../search.json',
searchResultTemplate: '<li><a href="{url}" title="Application console">{title}</a></li>',
noResultsText: 'No results found.',
limit: 10,
fuzzy: true,
})
</script>
<!--end search-->
</li>
</div>
<!-- /.container -->
</nav>
<!-- Page Content -->
<div class="container">
<div class="col-lg-12">&nbsp;</div>
<!-- Content Row -->
<div class="row">
<!-- Sidebar Column -->
<div class="col-md-3">
<script>
$(document).ready(function() {
// Initialize navgoco with default options
$("#mysidebar").navgoco({
caretHtml: '',
accordion: true,
openClass: 'active', // open
save: true,
cookie: {
name: 'navgoco',
expires: false,
path: '/'
},
slide: {
duration: 400,
easing: 'swing'
}
});
$("#collapseAll").click(function(e) {
e.preventDefault();
$("#mysidebar").navgoco('toggle', false);
});
$("#expandAll").click(function(e) {
e.preventDefault();
$("#mysidebar").navgoco('toggle', true);
});
});
</script>
<ul id="mysidebar" class="nav">
<span class="siteTagline">Edgent</span>
<span class="versionTagline">Version 1.2.0-incubating</span>
<li><a href="#">Overview</a>
<ul>
<li><a href="../docs/edgent_index.html">Introduction</a></li>
<li><a href="../docs/home.html">Edgent Overview</a></li>
<li><a href="../docs/power-of-edgent.html">The Power of Edgent</a></li>
<li><a href="../docs/faq.html">FAQ</a></li>
</ul>
<li><a href="#">Get Started</a>
<ul>
<li><a href="../docs/downloads.html">Downloads</a></li>
<li><a href="../docs/edgent-getting-started.html">Getting Started Guide</a></li>
<li><a href="../docs/edgent-getting-started-samples.html">Quickstart with Edgent Samples</a></li>
<li><a href="../docs/application-development.html">Understanding App Development</a></li>
<li><a href="../docs/quickstart.html">Quickstart IBM Watson IoT Platform</a></li>
<li><a href="../docs/streaming-concepts.html">Streaming concepts</a></li>
<li><a href="../docs/common-edgent-operations.html">Common operations</a></li>
</ul>
<li><a href="#">Edgent Cookbook</a>
<ul>
<li><a href="../recipes/recipe_hello_edgent.html">Hello Edgent!</a></li>
<li><a href="../recipes/recipe_source_function.html">Writing a source function</a></li>
<li><a href="../recipes/recipe_value_out_of_range.html">Detecting a sensor value out of expected range</a></li>
<li><a href="../recipes/recipe_different_processing_against_stream.html">Applying different processing against a single stream</a></li>
<li><a href="../recipes/recipe_combining_streams_processing_results.html">Splitting a stream to apply different processing and combining the results into a single stream</a></li>
<li><a href="../recipes/recipe_external_filter_range.html">Using an external configuration file for filter ranges</a></li>
<li><a href="../recipes/recipe_adaptable_filter_range.html">Changing a filter's range</a></li>
<li><a href="../recipes/recipe_adaptable_polling_source.html">Changing a polled source stream's period</a></li>
<li><a href="../recipes/recipe_adaptable_deadtime_filter.html">Using an adaptable deadtime filter</a></li>
<li><a href="../recipes/recipe_dynamic_analytic_control.html">Dynamically enabling analytic flows</a></li>
<li><a href="../recipes/recipe_parallel_analytics.html">How can I run analytics on several tuples in parallel?</a></li>
<li><a href="../recipes/recipe_concurrent_analytics.html">How can I run several analytics on a tuple concurrently?</a></li>
<li><a href="../recipes/recipe_writing_a_connector.html">How do I write a connector?</a></li>
</ul>
<li><a href="#">Using the Console</a>
<ul>
<li class="active"><a href="../docs/console.html">Using the console</a></li>
</ul>
<li><a href="#">Get Involved</a>
<ul>
<li><a href="../docs/community.html">How to participate</a></li>
<li><a href="../docs/committers.html">Committers</a></li>
</ul>
<!-- if you aren't using the accordion, uncomment this block:
<p class="external">
<a href="#" id="collapseAll">Collapse All</a> | <a href="#" id="expandAll">Expand All</a>
</p>
-->
<br/>
</li>
</ul>
<div class="row">
<div class="col-md-12">
<!-- this handles the automatic toc. use ## for subheads to auto-generate the on-page minitoc. if you use html tags, you must supply an ID for the heading element in order for it to appear in the minitoc. -->
<script>
$( document ).ready(function() {
// Handler for .ready() called.
$('#toc').toc({ minimumHeaders: 0, listType: 'ul', showSpeed: 0, headers: 'h2,h3,h4' });
/* this offset helps account for the space taken up by the floating toolbar. */
$('#toc').on('click', 'a', function() {
var target = $(this.getAttribute('href'))
, scroll_target = target.offset().top
$(window).scrollTop(scroll_target - 10);
return false
})
});
</script>
<div id="toc"></div>
</div>
</div>
</div>
<!-- this highlights the active parent class in the navgoco sidebar. this is critical so that the parent expands when you're viewing a page. This must appear below the sidebar code above. Otherwise, if placed inside customscripts.js, the script runs before the sidebar code runs and the class never gets inserted.-->
<script>$("li.active").parents('li').toggleClass("active");</script>
<!-- Content Column -->
<div class="col-md-9">
<div class="post-header">
<h1 class="post-title-main">Application console</h1>
</div>
<div class="post-content">
<!-- this handles the automatic toc. use ## for subheads to auto-generate the on-page minitoc. if you use html tags, you must supply an ID for the heading element in order for it to appear in the minitoc. -->
<script>
$( document ).ready(function() {
// Handler for .ready() called.
$('#toc').toc({ minimumHeaders: 0, listType: 'ul', showSpeed: 0, headers: 'h2,h3,h4' });
/* this offset helps account for the space taken up by the floating toolbar. */
$('#toc').on('click', 'a', function() {
var target = $(this.getAttribute('href'))
, scroll_target = target.offset().top
$(window).scrollTop(scroll_target - 10);
return false
})
});
</script>
<div id="toc"></div>
<a target="_blank" href="https://github.com/apache/incubator-edgent-website/blob/master/site/docs/console.md" class="btn btn-default githubEditButton" role="button"><i class="fa fa-github fa-lg"></i> Edit me</a>
<h2 id="visualizing-and-monitoring-your-application">Visualizing and monitoring your application</h2>
<p>The Edgent application console is a web application that enables you to visualize your application topology and monitor the tuples flowing through your application. The kind of oplets used in the topology, as well as the stream tags included in the topology, are also visible in the console.</p>
<h2 id="adding-the-console-web-app-to-your-application">Adding the console web app to your application</h2>
<p>To use the console, you must use the Edgent classes that provide the service to access the console web application or directly call the <code>HttpServer</code> class itself, start the server and then obtain the console URL.</p>
<p>The easiest way to include the console in your application is to use the the <code>DevelopmentProvider</code> class. <code>DevelopmentProvider</code> is a subclass of <code>DirectProvider</code> and adds services such as access to the console web application and counter oplets used to determine tuple counts. You can get the URL for the console from the <code>DevelopmentProvider</code> using the <code>getService</code> method as shown in a hypothetical application shown below:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.util.concurrent.TimeUnit</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.edgent.console.server.HttpServer</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.edgent.providers.development.DevelopmentProvider</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.edgent.topology.TStream</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.edgent.topology.Topology</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">TempSensorApplication</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
<span class="n">TempSensor</span> <span class="n">sensor</span> <span class="o">=</span> <span class="k">new</span> <span class="n">TempSensor</span><span class="o">();</span>
<span class="n">DevelopmentProvider</span> <span class="n">dp</span> <span class="o">=</span> <span class="k">new</span> <span class="n">DevelopmentProvider</span><span class="o">();</span>
<span class="n">Topology</span> <span class="n">topology</span> <span class="o">=</span> <span class="n">dp</span><span class="o">.</span><span class="na">newTopology</span><span class="o">();</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span> <span class="n">tempReadings</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">poll</span><span class="o">(</span><span class="n">sensor</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">MILLISECONDS</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span> <span class="n">filteredReadings</span> <span class="o">=</span> <span class="n">tempReadings</span><span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">reading</span> <span class="o">-&gt;</span> <span class="n">reading</span> <span class="o">&lt;</span> <span class="mi">50</span> <span class="o">||</span> <span class="n">reading</span> <span class="o">&gt;</span> <span class="mi">80</span><span class="o">);</span>
<span class="n">filteredReadings</span><span class="o">.</span><span class="na">print</span><span class="o">();</span>
<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">dp</span><span class="o">.</span><span class="na">getServices</span><span class="o">().</span><span class="na">getService</span><span class="o">(</span><span class="n">HttpServer</span><span class="o">.</span><span class="na">class</span><span class="o">).</span><span class="na">getConsoleUrl</span><span class="o">());</span>
<span class="n">dp</span><span class="o">.</span><span class="na">submit</span><span class="o">(</span><span class="n">topology</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>Note that the console URL is being printed to <code>System.out</code>. The <code>filteredReadings</code> are as well, since <code>filteredReadings.print()</code> is being called in the application. You may need to scroll your terminal window up to see the output for the console URL.</p>
<p>Optionally, you can modify the above code in the application to have a timeout before submitting the topology, which would allow you to see the console URL before any other output is shown. The modification would look like this:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">// Print the console URL and wait for 10 seconds before submitting the topology</span>
<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">dp</span><span class="o">.</span><span class="na">getServices</span><span class="o">().</span><span class="na">getService</span><span class="o">(</span><span class="n">HttpServer</span><span class="o">.</span><span class="na">class</span><span class="o">).</span><span class="na">getConsoleUrl</span><span class="o">());</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="mi">10</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">InterruptedException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// Do nothing</span>
<span class="o">}</span>
<span class="n">dp</span><span class="o">.</span><span class="na">submit</span><span class="o">(</span><span class="n">topology</span><span class="o">);</span>
</code></pre></div>
<p>The other way to embed the console in your application is shown in the <code>HttpServerSample.java</code> example (on <a href="https://github.com/apache/incubator-edgent/blob/master/samples/console/src/main/java/org/apache/edgent/samples/console/HttpServerSample.java">GitHub</a>). It gets the <code>HttpServer</code> instance, starts it, and prints out the console URL. Note that it does not submit a job, so when the console is displayed in the browser, there are no running jobs and therefore no topology graph. The example is meant to show how to get the <code>HttpServer</code> instance, start the console web app and get the URL of the console.</p>
<h2 id="accessing-the-console">Accessing the console</h2>
<p>The console URL has the following format:</p>
<p><code>http://host_name:port_number/console</code></p>
<p>Once it is obtained from <code>System.out</code>, enter it in a browser window.</p>
<p>If you cannot access the console at this URL, ensure there is a <code>console.war</code> file in the <code>webapps</code> directory. If the <code>console.war</code> file cannot be found, an exception will be thrown (in <code>std.out</code>) indicating <code>console.war</code> was not found.</p>
<h2 id="consolewaterdetector-sample">ConsoleWaterDetector sample</h2>
<p>To see the features of the console in action and as a way to demonstrate how to monitor a topology in the console, let&#39;s look at the <code>ConsoleWaterDetector</code> sample (on <a href="https://github.com/apache/incubator-edgent/blob/master/samples/console/src/main/java/org/apache/edgent/samples/console/ConsoleWaterDetector.java">GitHub</a>).</p>
<p>Prior to running any console applications, the <code>console.war</code> file must be built as mentioned above. If you are building Edgent from a Git repository, go to the top level Edgent directory and run <code>ant</code>.</p>
<p>Here is an example in my environment:</p>
<div class="highlight"><pre><code class="language-" data-lang="">Susans-MacBook-Pro-247:edgent susancline$ pwd
/Users/susancline/git/edgent
Susans-MacBook-Pro-247:edgent susancline$ ant
Buildfile: /Users/susancline/git/edgent/build.xml
setcommitversion:
init:
suball:
init:
project.component:
compile:
...
[javadoc] Constructing Javadoc information...
[javadoc] Standard Doclet version 1.8.0_71
[javadoc] Building tree for all the packages and classes...
[javadoc] Generating /Users/susancline/git/edgent/target/docs/javadoc/edgent/analytics/sensors/package-summary.html...
[javadoc] Copying file /Users/susancline/git/edgent/analytics/sensors/src/main/java/edgent/analytics/sensors/doc-files/deadband.png to directory /Users/susancline/git/edgent/target/docs/javadoc/edgent/analytics/sensors/doc-files...
[javadoc] Generating /Users/susancline/git/edgent/target/docs/javadoc/edgent/topology/package-summary.html...
[javadoc] Copying file /Users/susancline/git/edgent/api/topology/src/main/java/edgent/topology/doc-files/sources.html to directory /Users/susancline/git/edgent/target/docs/javadoc/edgent/topology/doc-files...
[javadoc] Building index for all the packages and classes...
[javadoc] Building index for all classes...
all:
BUILD SUCCESSFUL
Total time: 3 seconds
</code></pre></div>
<p>This command will let you know that <code>console.war</code> was built and is in the correct place, under the <code>webapps</code> directory.</p>
<div class="highlight"><pre><code class="language-" data-lang="">Susans-MacBook-Pro-247:edgent susancline$ find . -name console.war -print
./target/java8/console/webapps/console.war
</code></pre></div>
<p>Now we know we have built <code>console.war</code>, so we&#39;re good to go. To run this sample from the command line:</p>
<div class="highlight"><pre><code class="language-" data-lang="">Susans-MacBook-Pro-247:edgent susancline$ pwd
/Users/susancline/git/edgent
Susans-MacBook-Pro-247:edgent susancline$ java -cp target/java8/samples/lib/edgent.samples.console.jar:. edgent.samples.console.ConsoleWaterDetector
</code></pre></div>
<p>If everything is successful, you&#39;ll start seeing output. You may have to scroll back up to get the URL of the console:</p>
<div class="highlight"><pre><code class="language-" data-lang="">Susans-MacBook-Pro-247:edgent susancline$ java -cp target/java8/samples/lib/edgent.samples.console.jar:. edgent.samples.console.ConsoleWaterDetector
Mar 07, 2016 12:04:52 PM org.eclipse.jetty.util.log.Log initialized
INFO: Logging initialized @176ms
Mar 07, 2016 12:04:53 PM org.eclipse.jetty.server.Server doStart
INFO: jetty-9.3.6.v20151106
Mar 07, 2016 12:04:53 PM org.eclipse.jetty.server.handler.ContextHandler doStart
INFO: Started o.e.j.s.ServletContextHandler@614c5515{/jobs,null,AVAILABLE}
Mar 07, 2016 12:04:53 PM org.eclipse.jetty.server.handler.ContextHandler doStart
INFO: Started o.e.j.s.ServletContextHandler@77b52d12{/metrics,null,AVAILABLE}
Mar 07, 2016 12:04:53 PM org.eclipse.jetty.webapp.StandardDescriptorProcessor visitServlet
INFO: NO JSP Support for /console, did not find org.eclipse.jetty.jsp.JettyJspServlet
Mar 07, 2016 12:04:53 PM org.eclipse.jetty.server.handler.ContextHandler doStart
INFO: Started o.e.j.w.WebAppContext@2d554825{/console,file:///private/var/folders/0c/pb4rznhj7sbc886t30w4vpxh0000gn/T/jetty-0.0.0.0-0-console.war-_console-any-3101338829524954950.dir/webapp/,AVAILABLE}{/console.war}
Mar 07, 2016 12:04:53 PM org.eclipse.jetty.server.AbstractConnector doStart
INFO: Started ServerConnector@66480dd7{HTTP/1.1,[http/1.1]}{0.0.0.0:57964}
Mar 07, 2016 12:04:53 PM org.eclipse.jetty.server.Server doStart
INFO: Started @426ms
http://localhost:57964/console
Well1 alert, ecoli value is 1
Well1 alert, temp value is 48
Well3 alert, ecoli value is 1
</code></pre></div>
<p>Now point your browser to the URL displayed above in the output from running the Java command to launch the <code>ConsoleWaterDetector</code> application. In this case, the URL is <code>http://localhost:57964/console</code>.</p>
<p>Below is a screen shot of what you should see if everything is working properly:</p>
<p><img src='images/console_overview.jpg' alt='First view of the ConsoleWaterDetector app in the console' width='100%'/></p>
<h2 id="consolewaterdetector-application-scenario">ConsoleWaterDetector application scenario</h2>
<p>The application is now running in your browser. Let&#39;s discuss the scenario for the application.</p>
<p>A county agency is responsible for ensuring the safety of residents well water. Each well they monitor has four different sensor types:</p>
<ul>
<li>Temperature</li>
<li>Acidity</li>
<li>Ecoli</li>
<li>Lead</li>
</ul>
<p>The sample application topology monitors 3 wells:</p>
<ul>
<li>For the hypothetical scenario, Well1 and Well3 produce &#39;unhealthy&#39; values from their sensors on occasion. Well2 always produces &#39;healthy&#39; values.</li>
<li>Each well that is to be measured is added to the topology. The topology polls each sensor (temp, ecoli, etc.) for each well as a unit. A <code>TStream&lt;Integer&gt;</code> is returned from polling the toplogy and represents a sensor reading. Each sensor reading for the well has a tag added to it with the reading type i.e, &quot;temp&quot;, and the well id. Once all of the sensor readings are obtained and the tags added, each sensor reading is &#39;unioned&#39; into a single <code>TStream&lt;JsonObject&gt;</code>. Look at the <code>waterDetector</code> method for details on this.</li>
<li>Now, each well has a single stream with each of the sensors readings as a property with a name and value in the <code>TStream&lt;JsonObject&gt;</code>. Next the <code>alertFilter</code> method is called on the <code>TStream&lt;JsonObject&gt;</code> representing each well. This method checks the values for each well&#39;s sensors to determine if they are &#39;out of range&#39; for healthy values. The <code>filter</code> oplet is used to do this. If any of the sensor&#39;s readings are out of the acceptable range the tuple is passed along. Those that are within an acceptable range are discarded.</li>
<li><p>Next the applications&#39; <code>splitAlert</code> method is called on each well&#39;s stream that contains the union of all the sensor readings that are out of range. The <code>splitAlert</code> method uses the <code>split</code> oplet to split the incoming stream into 5 different streams. Only those tuples that are out of range for each stream, which represents each sensor type, will be returned. The object returned from <code>splitAlert</code> is a list of <code>TStream&lt;JsonObject&gt;</code> objects. The <code>splitAlert</code> method is shown below:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">static</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;&gt;</span> <span class="nf">splitAlert</span><span class="o">(</span><span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;</span> <span class="n">alertStream</span><span class="o">,</span> <span class="kt">int</span> <span class="n">wellId</span><span class="o">)</span> <span class="o">{</span>
<span class="n">List</span><span class="o">&lt;</span><span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;&gt;</span> <span class="n">allStreams</span> <span class="o">=</span> <span class="n">alertStream</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="mi">5</span><span class="o">,</span> <span class="n">tuple</span> <span class="o">-&gt;</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"temp"</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">JsonObject</span> <span class="n">tempObj</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonObject</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"temp"</span><span class="o">).</span><span class="na">getAsInt</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">temp</span> <span class="o">&lt;=</span> <span class="n">TEMP_ALERT_MIN</span> <span class="o">||</span> <span class="n">temp</span> <span class="o">&gt;=</span> <span class="n">TEMP_ALERT_MAX</span><span class="o">)</span> <span class="o">{</span>
<span class="n">tempObj</span><span class="o">.</span><span class="na">addProperty</span><span class="o">(</span><span class="s">"temp"</span><span class="o">,</span> <span class="n">temp</span><span class="o">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"acidity"</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">){</span>
<span class="n">JsonObject</span> <span class="n">acidObj</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonObject</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">acid</span> <span class="o">=</span> <span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"acidity"</span><span class="o">).</span><span class="na">getAsInt</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">acid</span> <span class="o">&lt;=</span> <span class="n">ACIDITY_ALERT_MIN</span> <span class="o">||</span> <span class="n">acid</span> <span class="o">&gt;=</span> <span class="n">ACIDITY_ALERT_MAX</span><span class="o">)</span> <span class="o">{</span>
<span class="n">acidObj</span><span class="o">.</span><span class="na">addProperty</span><span class="o">(</span><span class="s">"acidity"</span><span class="o">,</span> <span class="n">acid</span><span class="o">);</span>
<span class="k">return</span> <span class="mi">1</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"ecoli"</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">JsonObject</span> <span class="n">ecoliObj</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonObject</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">ecoli</span> <span class="o">=</span> <span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"ecoli"</span><span class="o">).</span><span class="na">getAsInt</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">ecoli</span> <span class="o">&gt;=</span> <span class="n">ECOLI_ALERT</span><span class="o">)</span> <span class="o">{</span>
<span class="n">ecoliObj</span><span class="o">.</span><span class="na">addProperty</span><span class="o">(</span><span class="s">"ecoli"</span><span class="o">,</span> <span class="n">ecoli</span><span class="o">);</span>
<span class="k">return</span> <span class="mi">2</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"lead"</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">JsonObject</span> <span class="n">leadObj</span> <span class="o">=</span> <span class="k">new</span> <span class="n">JsonObject</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">lead</span> <span class="o">=</span> <span class="n">tuple</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"lead"</span><span class="o">).</span><span class="na">getAsInt</span><span class="o">();</span>
<span class="k">if</span> <span class="o">(</span><span class="n">lead</span> <span class="o">&gt;=</span> <span class="n">LEAD_ALERT_MAX</span><span class="o">)</span> <span class="o">{</span>
<span class="n">leadObj</span><span class="o">.</span><span class="na">addProperty</span><span class="o">(</span><span class="s">"lead"</span><span class="o">,</span> <span class="n">lead</span><span class="o">);</span>
<span class="k">return</span> <span class="mi">3</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">});</span>
<span class="k">return</span> <span class="n">allStreams</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></li>
<li><p>Next we want to get the temperature stream from the first well and put a rate meter on it to determine the rate at which the out of range values are flowing in the stream</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">List</span><span class="o">&lt;</span><span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;&gt;</span> <span class="n">individualAlerts1</span> <span class="o">=</span> <span class="n">splitAlert</span><span class="o">(</span><span class="n">filteredReadings1</span><span class="o">,</span> <span class="mi">1</span><span class="o">);</span>
<span class="c1">// Put a rate meter on well1's temperature sensor output</span>
<span class="n">Metrics</span><span class="o">.</span><span class="na">rateMeter</span><span class="o">(</span><span class="n">individualAlerts1</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">));</span>
</code></pre></div></li>
<li><p>Next all the sensors for well 1 have tags added to the stream indicating the stream is out of range for that sensor and the well id. Next a sink is added, passing the tuple to a <code>Consumer</code> that formats a string to <code>System.out</code> containing the well id, alert type (sensor type) and value of the sensor.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="c1">// Put a rate meter on well1's temperature sensor output</span>
<span class="n">Metrics</span><span class="o">.</span><span class="na">rateMeter</span><span class="o">(</span><span class="n">individualAlerts1</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">));</span>
<span class="n">individualAlerts1</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">).</span><span class="na">tag</span><span class="o">(</span><span class="n">TEMP_ALERT_TAG</span><span class="o">,</span> <span class="s">"well1"</span><span class="o">).</span><span class="na">sink</span><span class="o">(</span><span class="n">tuple</span> <span class="o">-&gt;</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"\n"</span> <span class="o">+</span> <span class="n">formatAlertOutput</span><span class="o">(</span><span class="n">tuple</span><span class="o">,</span> <span class="s">"1"</span><span class="o">,</span> <span class="s">"temp"</span><span class="o">)));</span>
<span class="n">individualAlerts1</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">).</span><span class="na">tag</span><span class="o">(</span><span class="n">ACIDITY_ALERT_TAG</span><span class="o">,</span> <span class="s">"well1"</span><span class="o">).</span><span class="na">sink</span><span class="o">(</span><span class="n">tuple</span> <span class="o">-&gt;</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">formatAlertOutput</span><span class="o">(</span><span class="n">tuple</span><span class="o">,</span> <span class="s">"1"</span><span class="o">,</span> <span class="s">"acidity"</span><span class="o">)));</span>
<span class="n">individualAlerts1</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">).</span><span class="na">tag</span><span class="o">(</span><span class="n">ECOLI_ALERT_TAG</span><span class="o">,</span> <span class="s">"well1"</span><span class="o">).</span><span class="na">sink</span><span class="o">(</span><span class="n">tuple</span> <span class="o">-&gt;</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">formatAlertOutput</span><span class="o">(</span><span class="n">tuple</span><span class="o">,</span> <span class="s">"1"</span><span class="o">,</span> <span class="s">"ecoli"</span><span class="o">)));</span>
<span class="n">individualAlerts1</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">3</span><span class="o">).</span><span class="na">tag</span><span class="o">(</span><span class="n">LEAD_ALERT_TAG</span><span class="o">,</span> <span class="s">"well1"</span><span class="o">).</span><span class="na">sink</span><span class="o">(</span><span class="n">tuple</span> <span class="o">-&gt;</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">formatAlertOutput</span><span class="o">(</span><span class="n">tuple</span><span class="o">,</span> <span class="s">"1"</span><span class="o">,</span> <span class="s">"lead"</span><span class="o">)));</span>
</code></pre></div></li>
</ul>
<p>Output in the terminal window from the <code>formatAlertOutput</code> method will look like this:</p>
<div class="highlight"><pre><code class="language-" data-lang="">Well1 alert, temp value is 86
Well3 alert, ecoli value is 2
Well1 alert, ecoli value is 1
Well3 alert, acidity value is 1
Well1 alert, lead value is 12
Well1 alert, ecoli value is 2
Well3 alert, lead value is 10
Well3 alert, acidity value is 10
</code></pre></div>
<p>Notice how only those streams that are out of range for the temperature sensor type show output.</p>
<h2 id="detecting-zero-tuple-counts">Detecting zero tuple counts</h2>
<p>At the end of the <code>ConsoleWaterDetector</code> application is this snippet of code, added after the topology has been submitted:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">dp</span><span class="o">.</span><span class="na">submit</span><span class="o">(</span><span class="n">wellTopology</span><span class="o">);</span>
<span class="k">while</span> <span class="o">(</span><span class="kc">true</span><span class="o">)</span> <span class="o">{</span>
<span class="n">MetricRegistry</span> <span class="n">metricRegistry</span> <span class="o">=</span> <span class="n">dp</span><span class="o">.</span><span class="na">getServices</span><span class="o">().</span><span class="na">getService</span><span class="o">(</span><span class="n">MetricRegistry</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">SortedMap</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Counter</span><span class="o">&gt;</span> <span class="n">counters</span> <span class="o">=</span> <span class="n">metricRegistry</span><span class="o">.</span><span class="na">getCounters</span><span class="o">();</span>
<span class="n">Set</span><span class="o">&lt;</span><span class="n">Entry</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Counter</span><span class="o">&gt;&gt;</span> <span class="n">values</span> <span class="o">=</span> <span class="n">counters</span><span class="o">.</span><span class="na">entrySet</span><span class="o">();</span>
<span class="k">for</span> <span class="o">(</span><span class="n">Entry</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">Counter</span><span class="o">&gt;</span> <span class="n">e</span> <span class="o">:</span> <span class="n">values</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">getValue</span><span class="o">().</span><span class="na">getCount</span><span class="o">()</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Counter Op:"</span> <span class="o">+</span> <span class="n">e</span><span class="o">.</span><span class="na">getKey</span><span class="o">()</span> <span class="o">+</span> <span class="s">" tuple count: "</span> <span class="o">+</span> <span class="n">e</span><span class="o">.</span><span class="na">getValue</span><span class="o">().</span><span class="na">getCount</span><span class="o">());</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="n">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="mi">2000</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div>
<p>What this does is get all the counters in the <code>MetricRegistry</code> class and print out the name of the counter oplet they are monitoring along with the tuple count if it is zero. Here is some sample output:</p>
<div class="highlight"><pre><code class="language-" data-lang="">Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_44 has a tuple count of zero!
Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_45 has a tuple count of zero!
Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_46 has a tuple count of zero!
Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_47 has a tuple count of zero!
Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_89 has a tuple count of zero!
Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_95 has a tuple count of zero!
Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_96 has a tuple count of zero!
Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_97 has a tuple count of zero!
Counter Op:TupleCounter.edgent.oplet.JOB_0.OP_98 has a tuple count of zero!
</code></pre></div>
<p>To summarize what the application is doing:</p>
<ul>
<li>Unions all sensor type readings for a single well</li>
<li>Filters all sensor type readings for a single well, passing on an object that only contains tuples for the object that have at least one sensor type with out of range values</li>
<li>Splits the object that contained name/value pairs for sensor type and readings into individual sensor types returning only those streams that contain out of range values</li>
<li>Outputs to the command line the well id, sensor type and value that is out of range</li>
<li>Tags are added at various points in the topology for easier identification of either the well or some out of range condition</li>
<li>The topology contains counters to measure tuple counts since <code>DevelopmentProvider</code> was used</li>
<li>Individual rate meters were placed on <code>well1</code> and <code>well3</code>&#39;s temperature sensors to determine the rate of &#39;unhealthy&#39; values</li>
<li>Prints out the name of the counter oplets whose tuple counts are zero</li>
</ul>
<h2 id="topology-graph-controls">Topology graph controls</h2>
<p>Now that you have an understanding of what the application is doing, let&#39;s look at some of the controls in the console, so we can learn how to monitor the application. Below is a screen shot of the top controls: the controls that affect the Topology Graph.</p>
<p><img src='images/console_top_controls.jpg' alt='The controls that impact the topology graph' width='100%'/></p>
<ul>
<li><strong>Job</strong>: A drop down to select which job is being displayed in the Topology Graph. An application can contain multiple jobs.</li>
<li><strong>State</strong>: Hovering over the &#39;State&#39; icon shows information about the selected job. The current and next states of the job, the job id and the job name.</li>
<li><strong>View by</strong>: This select is used to change how the topology graph is displayed. The three options for this select are:
<ul>
<li>Static flow</li>
<li>Tuple count</li>
<li>Oplet kind</li>
<li>Currently it is set to &#39;Static flow&#39;. This means the oplets (represented as circles in the topology graph) do not change size, nor do the lines or links (representing the edges of the topology graph) change width or position. The graph is not being refreshed when it is in &#39;Static flow&#39; mode.</li>
</ul></li>
<li><strong>Refresh interval</strong>: Allows the user to select an interval between 3 - 20 seconds to refresh the tuple count values in the graph. Every X seconds the metrics for the topology graph are refreshed. More about metrics a little bit later.</li>
<li><strong>Pause graph</strong>: Stops the refresh interval timer. Once the &#39;Pause graph&#39; button is clicked, the user must push &#39;Resume graph&#39; for the graph to be updated, and then refreshed at the interval set in the &#39;Refresh interval&#39; timer. It can be helpful to pause the graph if multiple oplets are occupying the same area on the graph, and their names become unreadable. Once the graph is paused, the user can drag an oplet off of another oplet to better view the name and see the edge(s) that connect them.</li>
<li><strong>Show tags</strong>: If the checkbox appears in the top controls, it means:
<ul>
<li>The &#39;View by&#39; layer is capable of displaying stream tags</li>
<li>The topology currently shown in the topology graph has stream tags associated with it</li>
</ul></li>
<li><p><strong>Show all tags</strong>: Selecting this checkbox shows all the tags present in the topology. If you want to see only certain tags, uncheck this box and select the button labeled &#39;Select individual tags ...&#39;. A dialog will appear, and you can select one or all of the tags listed in the dialog which are present in the topology.</p>
<p><img src='images/console_select_individual_tags.jpg'/></p></li>
</ul>
<p>The next aspect of the console we&#39;ll look at are the popups available when selecting &#39;View all oplet properties&#39;, hovering over an oplet and hovering over an edge (link).</p>
<p>The screen shot below shows the output from clicking on the &#39;View all oplet properties&#39; link directly below the job selector:</p>
<p><img src='images/console_oplet_properties.jpg' alt='Displays a table showing the relationship between the oplets and vertices' width='100%'/></p>
<p>Looking at the sixth line in the table, where the Name is &#39;OP_5&#39;, we can see that the Oplet kind is a <code>Map</code>, a <code>edgent.oplet.functional.Map</code>, the Tuple count is 0 (this is because the view is in Static flow mode - the graph does not show the number of tuples flowing in it), the source oplet is &#39;OP_55&#39;, the target oplet is &#39;OP_60&#39;, and there are no stream tags coming from the source or target streams. Relationships for all oplets can be viewed in this manner.</p>
<p>Now, looking at the graph, if we want to see the relationships for a single oplet, we can hover over it. The image below shows the hover when we are over &#39;OP_5&#39;.</p>
<p><img src='images/console_hover_over_op.jpg' width='100%'/></p>
<p>You can also hover over the edges of the topology graph to get information. Hover over the edge (link) between &#39;OP_0&#39; and &#39;OP_55&#39;. The image shows the name and kind of the oplet as the source, and the name and kind of oplet as the target. Again the tuple count is 0 since this is the &#39;Static flow&#39; view. The last item of information in the tooltip is the tags on the stream.</p>
<p>One or many tags can be added to a stream. In this case we see the tags &#39;temperature&#39; and &#39;well1&#39;.</p>
<p><img src='images/console_hover_over_link.jpg'/></p>
<p>The section of the code that adds the tags &#39;temperature&#39; and &#39;well1&#39; is in the <code>waterDetector</code> method of the <code>ConsoleWaterDetector</code> class.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">static</span> <span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;</span> <span class="nf">waterDetector</span><span class="o">(</span><span class="n">Topology</span> <span class="n">topology</span><span class="o">,</span> <span class="kt">int</span> <span class="n">wellId</span><span class="o">)</span> <span class="o">{</span>
<span class="n">Random</span> <span class="n">rNum</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Random</span><span class="o">();</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">poll</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="n">rNum</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="n">TEMP_RANDOM_HIGH</span> <span class="o">-</span> <span class="n">TEMP_RANDOM_LOW</span><span class="o">)</span> <span class="o">+</span> <span class="n">TEMP_RANDOM_LOW</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span> <span class="n">acidity</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">poll</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="n">rNum</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="n">ACIDITY_RANDOM_HIGH</span> <span class="o">-</span> <span class="n">ACIDITY_RANDOM_LOW</span><span class="o">)</span> <span class="o">+</span> <span class="n">ACIDITY_RANDOM_LOW</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span> <span class="n">ecoli</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">poll</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="n">rNum</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="n">ECOLI_RANDOM_HIGH</span> <span class="o">-</span> <span class="n">ECOLI_RANDOM_LOW</span><span class="o">)</span> <span class="o">+</span> <span class="n">ECOLI_RANDOM_LOW</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span> <span class="n">lead</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">poll</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="n">rNum</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="n">LEAD_RANDOM_HIGH</span> <span class="o">-</span> <span class="n">LEAD_RANDOM_LOW</span><span class="o">)</span> <span class="o">+</span> <span class="n">LEAD_RANDOM_LOW</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">Integer</span><span class="o">&gt;</span> <span class="n">id</span> <span class="o">=</span> <span class="n">topology</span><span class="o">.</span><span class="na">poll</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="n">wellId</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">);</span>
<span class="c1">// add tags to each sensor</span>
<span class="n">temp</span><span class="o">.</span><span class="na">tag</span><span class="o">(</span><span class="s">"temperature"</span><span class="o">,</span> <span class="s">"well"</span> <span class="o">+</span> <span class="n">wellId</span><span class="o">);</span>
</code></pre></div>
<h3 id="legend">Legend</h3>
<p>The legend(s) that appear in the console depend on the view currently displayed. In the static flow mode, if no stream tags are present, there is no legend. In this example we have stream tags in the topology, so the static flow mode gives us the option to select &#39;Show tags&#39;. If selected, the result is the addition of the stream tags legend:</p>
<p><img src='images/console_stream_tags_legend.jpg'/></p>
<p>This legend shows all the tags that have been added to the topology, regardless of whether or not &#39;Show all tags&#39; is checked or specific tags have been selected from the dialog that appears when the &#39;Select individual tags ...&#39; button is clicked.</p>
<h3 id="topology-graph">Topology graph</h3>
<p>Now that we&#39;ve covered most of the ways to modify the view of the topology graph and discussed the application, let&#39;s look at the topology graph as a way to understand our application.</p>
<p>When analyzing what is happening in your application, here are some ways you might use the console to help you understand it:</p>
<ul>
<li>Topology of the application - how the edges and vertices of the graph are related</li>
<li>Tuple flow - tuple counts since the application was started</li>
<li>The affect of filters or maps on the downstream streams</li>
<li>Stream tags - if tags are added dynamically based on a condition, where the streams with tags are displayed in the topology</li>
</ul>
<p>Let&#39;s start with the static flow view of the topology. We can look at the graph, and we can also hover over any of the oplets or streams to better understand the connections. Also, we can click &#39;View all oplet properties&#39; and see the relationships in a tabular format.</p>
<p>The other thing to notice in the static flow view are the tags. Look for any colored edges (the links between the oplets). All of the left-most oplets have streams with tags. Most of them have the color that corresponds to &#39;Multiple tags&#39;. If you hover over the edges, you can see the tags. It&#39;s obvious that we have tagged each sensor with the sensor type and the well id.</p>
<p>Now, if you look to the far right, you can see more tags on streams coming out of a <code>split</code> oplet. They also have multiple tags, and hovering over them you can determine that they represent out of range values for each sensor type for the well. Notice how the <code>split</code> oplet, OP_43, has no tags in the streams coming out of it. If you follow that split oplet back, you can determine from the first tags that it is part of the well 2 stream.</p>
<p>If you refer back to the <code>ConsoleWaterDetector</code> source, you can see that no tags were placed on the streams coming out of <code>well2</code>&#39;s split because they contained no out of range values.</p>
<p>Let&#39;s switch the view to Oplet kind now. It will make more clear which oplets are producing the streams with the tags on them. Below is an image of how the graph looks after switching to the Oplet kind view.</p>
<p><img src="images/console_oplet_kind.jpg" width='100%'/></p>
<p>In the Oplet kind view the links are all the same width, but the circles representing the oplets are sized according to tuple flow. Notice how the circles representing OP_10, OP_32 and OP_21 are large in relation to OP_80, OP_88 and OP_89. As a matter of fact, we can&#39;t even see the circle representing OP_89. Looking at OP_35 and then the Oplet kind legend, you can see by the color that it is a Filter oplet. This is because the filter that we used against <code>well2</code>, which is the stream that OP_35 is part of returned no tuples. This is a bit difficult to see. Let&#39;s look at the Tuple count view.</p>
<p>The Tuple count view will make it more clear that no tuples are following out of OP_35, which represents the filter for <code>well2</code> and only returns out of range values. You may recall that in this example <code>well2</code> returned no out of range values. Below is the screen shot of the graph in &#39;Tuple count&#39; view mode.</p>
<p><img src="images/console_tuple_count.jpg" width='100%'/></p>
<p>The topology graph oplets can sometimes sit on top of each other. If this is the case, pause the refresh and use your mouse to pull down on the oplets that are in the same position. This will allow you to see their name. Alternately, you can use the &#39;View all properties&#39; table to see the relationships between oplets.</p>
<h3 id="metrics">Metrics</h3>
<p>If you scroll the browser window down, you can see a Metrics section. This section appears when the application contains the following:</p>
<ul>
<li>A <code>DevelopmentProvider</code> is used; this automatically inserts counters on the streams of the topology</li>
<li>A <code>edgent.metrics.Metric.Counter</code> or <code>edgent.metrics.Metric.RateMeter</code> is added to an individual stream</li>
</ul>
<h2 id="counters">Counters</h2>
<p>In the <code>ConsoleWaterDetector</code> application we used a <code>DevelopmentProvider</code>. Therefore, counters were added to most streams (edges) with the following exceptions (from the <a href="/javadoc/latest/org/apache/edgent/metrics/Metrics.html#counter-org.apache.edgent.topology.TStream-">Javadoc</a> for <code>edgent.metrics.Metrics</code>):</p>
<p><em>Oplets are only inserted upstream from a FanOut oplet.</em></p>
<p><em>If a chain of Peek oplets exists between oplets A and B, a Metric oplet is inserted after the last Peek, right upstream from oplet B.</em></p>
<p><em>If a chain of Peek oplets is followed by a FanOut, a metric oplet is inserted between the last Peek and the FanOut oplet.
The implementation is not idempotent; previously inserted metric oplets are treated as regular graph vertices. Calling the method twice will insert a new set of metric oplets into the graph.</em></p>
<p>Also, the application inserts counters on <code>well2</code>&#39;s streams after the streams from the individual sensors were unioned and then split:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">List</span><span class="o">&lt;</span><span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;&gt;</span> <span class="n">individualAlerts2</span> <span class="o">=</span> <span class="n">splitAlert</span><span class="o">(</span><span class="n">filteredReadings2</span><span class="o">,</span> <span class="mi">2</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;</span> <span class="n">alert0Well2</span> <span class="o">=</span> <span class="n">individualAlerts2</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
<span class="n">alert0Well2</span> <span class="o">=</span> <span class="n">Metrics</span><span class="o">.</span><span class="na">counter</span><span class="o">(</span><span class="n">alert0Well2</span><span class="o">);</span>
<span class="n">alert0Well2</span><span class="o">.</span><span class="na">tag</span><span class="o">(</span><span class="s">"well2"</span><span class="o">,</span> <span class="s">"temp"</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;</span> <span class="n">alert1Well2</span> <span class="o">=</span> <span class="n">individualAlerts2</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="n">alert1Well2</span> <span class="o">=</span> <span class="n">Metrics</span><span class="o">.</span><span class="na">counter</span><span class="o">(</span><span class="n">alert1Well2</span><span class="o">);</span>
<span class="n">alert1Well2</span><span class="o">.</span><span class="na">tag</span><span class="o">(</span><span class="s">"well2"</span><span class="o">,</span> <span class="s">"acidity"</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;</span> <span class="n">alert2Well2</span> <span class="o">=</span> <span class="n">individualAlerts2</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span>
<span class="n">alert2Well2</span> <span class="o">=</span> <span class="n">Metrics</span><span class="o">.</span><span class="na">counter</span><span class="o">(</span><span class="n">alert2Well2</span><span class="o">);</span>
<span class="n">alert2Well2</span><span class="o">.</span><span class="na">tag</span><span class="o">(</span><span class="s">"well2"</span><span class="o">,</span> <span class="s">"ecoli"</span><span class="o">);</span>
<span class="n">TStream</span><span class="o">&lt;</span><span class="n">JsonObject</span><span class="o">&gt;</span> <span class="n">alert3Well2</span> <span class="o">=</span> <span class="n">individualAlerts2</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">3</span><span class="o">);</span>
<span class="n">alert3Well2</span> <span class="o">=</span> <span class="n">Metrics</span><span class="o">.</span><span class="na">counter</span><span class="o">(</span><span class="n">alert3Well2</span><span class="o">);</span>
<span class="n">alert3Well2</span><span class="o">.</span><span class="na">tag</span><span class="o">(</span><span class="s">"well2"</span><span class="o">,</span> <span class="s">"lead"</span><span class="o">);</span>
</code></pre></div>
<p>When looking at the select next to the label &#39;Metrics&#39;, make sure the &#39;Count, oplets OP_37, OP_49 ...&#39; is selected. This select compares all of the counters in the topology visualized as a bar graph. An image is shown below:</p>
<p><img src="images/console_counter_metrics_bar.jpg" width='100%'/></p>
<p>Hover over individual bars to get the value of the number of tuples flowing through that oplet since the application was started. You can also see the oplet name. You can see that some of the oplets have zero tuples flowing through them.</p>
<p>The bars that are the tallest and therefore have the highest tuple count are OP_76, OP_67 and OP_65. If you look back up to the topology graph, in the Tuple count view, you can see that the edges (streams) surrounding these oplets have the color that corresponds to the highest tuple count (in the pictures above that color is bright orange in the Tuple count legend).</p>
<h3 id="rate-meters">Rate meters</h3>
<p>The other type of metric we can look at are rate meter metrics. In the <code>ConsoleWaterDetector</code> application we added two rate meters here with the objective of comparing the rate of out of range readings between <code>well1</code> and <code>well3</code>:</p>
<div class="highlight"><pre><code class="language-" data-lang="">List&lt;TStream&lt;JsonObject&gt;&gt; individualAlerts1 = splitAlert(filteredReadings1, 1);
// Put a rate meter on well1's temperature sensor output
Metrics.rateMeter(individualAlerts1.get(0));
...
List&lt;TStream&lt;JsonObject&gt;&gt; individualAlerts3 = splitAlert(filteredReadings3, 3);
// Put a rate meter on well3's temperature sensor output
Metrics.rateMeter(individualAlerts3.get(0));
</code></pre></div>
<p>Rate meters contain the following metrics for each stream they are added to:</p>
<ul>
<li>Tuple count</li>
<li>The rate of change in the tuple count. The following rates are available for a single stream:
<ul>
<li>1 minute rate change</li>
<li>5 minute rate change</li>
<li>15 minute rate change</li>
<li>Mean rate change</li>
</ul></li>
</ul>
<p>Now change the Metrics select to the &#39;MeanRate&#39;. In our example these correspond to oplets OP_37 and OP_49:</p>
<p><img src="images/console_rate_metrics.jpg" width='100%'/></p>
<p>Hovering over the slightly larger bar, the one to the right, the name is OP_49. Looking at the topology graph and changing the view to &#39;Static flow&#39;, follow the edges back from OP_49 until you can see an edge with a tag on it. You can see that OP_49&#39;s source is OP_51, whose source is OP_99. The edge between OP_99 and it&#39;s source OP_48 has multiple tags. Hovering over this stream, the tags are &#39;TEMP out of range&#39; and &#39;well3&#39;.</p>
<p>If a single rate meter is placed on a stream, in addition to plotting a bar chart, a line chart over the last 20 measures can be viewed. For example, if I comment out the addition of the rate meter for <code>well1</code> and then rerun the application, the Metrics section will look like the image below. I selected the &#39;OneMinuteRate&#39; and &#39;Line chart&#39; for Chart type:</p>
<p><img src="images/console_metrics_line_chart.jpg" width='100%'/></p>
<h2 id="summary">Summary</h2>
<p>The intent of the information on this page is to help you understand the following:</p>
<ul>
<li>How to add the console application to an Edgent application</li>
<li>How to run the <code>ConsoleWaterDetector</code> sample</li>
<li>The design/architecture in the <code>ConsoleWaterDetector</code> application</li>
<li>The controls for the Topology graph are and what they do, including the different views of the graph</li>
<li>The legend for the graph</li>
<li>How to interpret the graph and use the tooltips over the edges and vertices, as well as the &#39;View all properties&#39; link</li>
<li>How to add counters and rate meters to a topology</li>
<li>How to use the metrics section to understand tuple counters and rate meters</li>
<li>How to correlate values from the metrics section with the topology graph</li>
</ul>
<p>The Edgent console will continue to evolve and improve. Please open an issue if you see a problem with the existing console, but more importantly add an issue if you have an idea of how to make the console better.</p>
<p>The more folks write Edgent applications and view them in the console, the more information we can gather from the community about what is needed in the console. Please consider making a contribution if there is a feature in the console that would really help you and others!</p>
<div class="tags">
</div>
<!--
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'idrbwjekyll'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
-->
</div>
<footer>
<div class="row">
<div class="col-lg-12 footer">
Site last
generated: Apr 3, 2019 <br/>
</div>
</div>
<br/>
<div class="row">
<div class="col-md-12">
<p class="small">Apache Edgent is an effort undergoing Incubation at The Apache Software
Foundation (ASF), sponsored by the Incubator. Incubation is required of all newly accepted projects
until a further review indicates that the infrastructure, communications, and decision making process
have stabilized in a manner consistent with other successful ASF projects. While incubation status is
not necessarily a reflection of the completeness or stability of the code, it does indicate that the
project has yet to be fully endorsed by the ASF.</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<p class="small">Copyright © 2016 The Apache Software Foundation. Licensed under the Apache
License, Version 2.0.
Apache, the Apache Feather logo, and the Apache Incubator project logo are trademarks of The Apache
Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their
respective owners.</p>
</div>
</div>
<div class="container">
<div class="row">
<div>
<img class="img-responsive center-block" src="../img/edgent_incubation.png" style="display: block; margin: auto;"alt="">
</div>
</div>
</footer>
</div><!-- /.row -->
</div> <!-- /.container -->
</body>
</html>