aboutsummaryrefslogtreecommitdiffstats
path: root/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js
diff options
context:
space:
mode:
Diffstat (limited to 'gui-pdp-monitoring/src/webapp/js/MonitoringChart.js')
-rw-r--r--gui-pdp-monitoring/src/webapp/js/MonitoringChart.js260
1 files changed, 260 insertions, 0 deletions
diff --git a/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js b/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js
new file mode 100644
index 0000000..514446b
--- /dev/null
+++ b/gui-pdp-monitoring/src/webapp/js/MonitoringChart.js
@@ -0,0 +1,260 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+import * as d3 from "d3";
+
+/*
+ * Create a chart and append it to a container
+ */
+function createChart(data, container, title, unit, lineStroke, nodeColour) {
+ // Set the dimensions of the canvas
+ var margin = {
+ top : 30,
+ right : 20,
+ bottom : 30,
+ left : 50
+ }, width = 600 - margin.left - margin.right, height = 270 - margin.top
+ - margin.bottom;
+
+ // Set the ranges
+ var x = d3.time.scale().range([ 0, width ]);
+ var y = d3.scale.linear().range([ height, 0 ]);
+
+ // Define the axes
+ var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5).innerTickSize(
+ -height).outerTickSize(0).tickPadding(10);
+
+ var yAxis = d3.svg.axis().scale(y).orient("left").ticks(10).innerTickSize(
+ -width).outerTickSize(0).tickPadding(10);
+
+ // Define the line
+ var valueline = d3.svg.line().x(function(d) {
+ return x(d.timestamp);
+ }).y(function(d) {
+ return y(d.value);
+ });
+
+ // Add the svg canvas to the container
+ var svg = d3.select(container).append("svg").attr("preserveAspectRatio",
+ "xMinYMin meet").attr("viewBox", "0 0 600 400").classed(
+ "svg-content-responsive", true).append("g").attr("transform",
+ "translate(" + margin.left + "," + margin.top + ")");
+
+ // Set the unit for the value
+ svg.attr("unit", unit);
+
+ // Format the data for the chart
+ data.forEach(function(d) {
+ d.timestamp = d.timestamp;
+ d.value = +d.value;
+ });
+
+ // Scale the range of the data
+ x.domain(d3.extent(data, function(d) {
+ return d.timestamp;
+ }));
+ y.domain([ 0, d3.max(data, function(d) {
+ return Math.ceil((d.value + 1) / 10) * 10;
+ }) ]);
+
+ // Set the colour of the line
+ if (!lineStroke) {
+ lineStroke = "#5fbadd"
+ }
+
+ // Set the colour of the circles
+ if (!nodeColour) {
+ nodeColour = "#00A9D4"
+ }
+
+ // Add the valueline path
+ svg.append("path").attr("class", "line").data(data).attr("unit", unit)
+ .attr("stroke", lineStroke).attr("d", valueline(data));
+
+ // Add the scatterplot
+ svg.selectAll("circle").data(data).enter().append("circle").attr("r", 3.5)
+ .attr("class", "circle").attr("fill", nodeColour).attr("cx",
+ function(d) {
+ return x(d.timestamp);
+ }).attr("cy", function(d) {
+ return y(d.value);
+ })
+
+ // Apply the tooltip to each node
+ .on(
+ "mouseover",
+ function(d) {
+ d3.select("body").select(".tooltip").transition()
+ .duration(50).style("opacity", 1);
+ d3.select("body").select(".tooltip").html(
+ formatDate(new Date(d.timestamp)) + "<br/>"
+ + d.value + (unit ? " " + unit : ""))
+ .style("left", (d3.event.pageX) + "px").style(
+ "top", (d3.event.pageY - 28) + "px");
+ }).on(
+ "mouseout",
+ function(d) {
+ d3.select("body").select(".tooltip").transition()
+ .duration(500).style("opacity", 0);
+ });
+
+ // Add the X Axis
+ svg.append("g").attr("class", "x axis").attr("transform",
+ "translate(0," + height + ")").call(xAxis);
+
+ // Add the Y Axis
+ svg.append("g").attr("class", "y axis").call(yAxis);
+
+ // Add the title
+ svg.append("text").attr("x", (width / 2)).attr("y", 0 - (margin.top / 2))
+ .attr("text-anchor", "middle").style("font-size", "16px").style(
+ "text-decoration", "underline").text(title);
+
+ // Add the background
+ svg.selectAll(".tick:not(:first-of-type) line").attr("stroke", "#777")
+ .attr("stroke-dasharray", "2,2");
+}
+
+/*
+ * Generates random chart data. Used when initializing the charts so that they
+ * are not empty on load
+ */
+function generateRandomData() {
+ var data = [];
+ for (var i = 0; i < 30; i++) {
+ data.push({
+ timestamp : new Date().getTime() - (i * 5000),
+ value : Math.floor(Math.random() * 100) + 1
+ });
+ }
+ return data;
+}
+
+/*
+ * Update a chart belonging to a specific container
+ */
+function updateChart(container, data, nodeColour) {
+ var margin = {
+ top : 30,
+ right : 20,
+ bottom : 30,
+ left : 50
+ }, width = 600 - margin.left - margin.right, height = 270 - margin.top
+ - margin.bottom;
+ var parseDate = d3.time.format("%d-%b-%y").parse;
+
+ // Format the data for the chart
+ data.forEach(function(d) {
+ d.timestamp = d.timestamp;
+ d.value = +d.value;
+ });
+
+ // Select the chart
+ var svg = d3.select(container);
+
+ // Set the ranges
+ var x = d3.time.scale().range([ 0, width ]);
+ var y = d3.scale.linear().range([ height, 0 ]);
+
+ // Define the axes
+ var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5).innerTickSize(
+ -height).outerTickSize(0).tickPadding(10);
+
+ var yAxis = d3.svg.axis().scale(y).orient("left").ticks(10).innerTickSize(
+ -width).outerTickSize(0).tickPadding(10);
+
+ // Scale the range of the data
+ x.domain(d3.extent(data, function(d) {
+ return d.timestamp;
+ }));
+ y.domain([ 0, d3.max(data, function(d) {
+ return Math.ceil((d.value + 1) / 10) * 10;
+ }) ]);
+
+ // Update the valueline path
+ var valueline = d3.svg.line().x(function(d) {
+ return x(d.timestamp);
+ }).y(function(d) {
+ return y(d.value);
+ });
+
+ var unit = svg.select(".line").attr("unit");
+
+ // Remove all nodes
+ svg.selectAll("circle").remove();
+
+ // Set the node colour if one is passed in
+ if (!nodeColour) {
+ nodeColour = "#00A9D4"
+ }
+
+ // Make the changes
+ svg.select(".line").data(data) // change the line
+ .transition().duration(750).attr("d", valueline(data));
+ svg.select(".x.axis") // change the x axis
+ .transition().duration(750).call(xAxis.ticks(5));
+ svg.select(".y.axis") // change the y axis
+ .transition().duration(750).call(yAxis);
+
+ // Redraw the nodes based on the new data
+ svg.select("svg").select("g").selectAll("circle").data(data).enter()
+ .append("circle").attr("r", 3.5).attr("class", "circle").attr(
+ "fill", nodeColour).attr("cx", function(d) {
+ return x(d.timestamp);
+ }).attr("cy", function(d) {
+ return y(d.value);
+ })
+
+ // Apply the tooltip to each node
+ .on(
+ "mouseover",
+ function(d) {
+ d3.select("body").select(".tooltip").transition()
+ .duration(50).style("opacity", 1);
+ d3.select("body").select(".tooltip").html(
+ formatDate(new Date(d.timestamp)) + "<br/>"
+ + d.value + (unit ? " " + unit : ""))
+ .style("left", (d3.event.pageX) + "px").style(
+ "top", (d3.event.pageY - 28) + "px");
+ }).on(
+ "mouseout",
+ function(d) {
+ d3.select("body").select(".tooltip").transition()
+ .duration(500).style("opacity", 0);
+ });
+
+}
+
+/*
+ * Initialize a singleton div used as a floating tooltip for all charts
+ */
+function initTooltip() {
+ d3.select("body").append("div").attr("class", "tooltip").attr("id",
+ "tooltip").style("opacity", 0);
+}
+
+/*
+ * Format a date object to string
+ */
+function formatDate(date) {
+ return date.toLocaleString().replace(',', '');
+}
+
+export { initTooltip, createChart, updateChart }; \ No newline at end of file