From 30c896841c65c78cb4cf048c0c26c8e62ee0cf05 Mon Sep 17 00:00:00 2001 From: Determinant Date: Sat, 27 Dec 2014 13:29:20 +0800 Subject: init --- assets/js/monitor.js | 352 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 assets/js/monitor.js (limited to 'assets/js/monitor.js') diff --git a/assets/js/monitor.js b/assets/js/monitor.js new file mode 100644 index 0000000..a92f753 --- /dev/null +++ b/assets/js/monitor.js @@ -0,0 +1,352 @@ +var ajax_site = 'ajax'; +var refresh_rate = 3000; +var send_timeout = 20000; +var retry_rate = 3000; + +function Blocker(sleeper) { + this.counter = 0; + this.waiting = false; + this.sleeper = sleeper; +} + +Blocker.prototype.setHook = function() { + var blocker = this; + return function() { + blocker.counter++; + } +} +Blocker.prototype.setNotifier = function(cb) { + var blocker = this; + return function() { + cb.apply(this, arguments); + if (--blocker.counter == 0 && blocker.waiting) + blocker.sleeper(); + } +}; + +Blocker.prototype.go = function() { + if (this.counter == 0) + this.sleeper(); + this.waiting = true; +} + +function send_request(on_success) { + $.ajax({ + url: ajax_site, + type: 'GET', + cache: false, + dataType: 'json', + async: true, + success: on_success, + timeout: send_timeout, + error: function(a, b, c) { + console.log("failed while connecting, reason: " + b); + setTimeout(function() { + send_request(on_success); + }, retry_rate); + } + }); +} + +function hex(x) { + return ("0" + parseInt(x).toString(16)).slice(-2); +} + +function random_range(min, max) { + return Math.random() * (max - min) + min; +} + +function random_color() { + var r = (random_range(0, 200) + 100) / 2; + var g = (random_range(0, 200) + 100) / 2; + var b = (random_range(0, 200) + 100) / 2; + return "#" + hex(r) + hex(g) + hex(b); +} + +function LineGraph() {} + +LineGraph.prototype.recalc_y_domain = function(data) { + var min = d3.min(data, function(d) { return d.y; }); + var max = d3.max(data, function(d) { return d.y; }); + var delta = max - min; + this.y.domain([min - delta / 2.0, max + delta / 3.0]); +} + +LineGraph.prototype.recalc_domain = function(data) { + this.x.domain(d3.extent(data, function(d) { return d.x; })); + this.recalc_y_domain(data); +} + +LineGraph.prototype.parse_data = function(d) { + var data = []; + for (var i = 0; i < d.records.length; i++) + { + var rec = d.records[i]; + data.push({x: i, y: +rec.rec[0], rid: rec.rid}); + } + return data; +} + +LineGraph.prototype.update = function(d, i, b) { + var graph = this; + var data = this.parse_data(d); + if (data.length == 0) return; + var pdata = this.pdata; + var svg = d3.select(this.elem).select("svg"); + var path = svg.select(".line"); + + var shift = pdata.length ? data[0].rid - pdata[0].rid : 0; + var add = pdata.length ? data[data.length - 1].rid - pdata[pdata.length - 1].rid : 0; + if (pdata.length == 0 || pdata[pdata.length - 1].rid < data[0].rid || + pdata[0] > data[data.length - 1].rid || shift < 0 || add < 0) + { + if (data.length == 1) + { + data = []; + console.log("clear"); + } + this.recalc_domain(data); + path.transition() + .duration(200) + .style("opacity", 1e-6) + .transition() + .attr("d", graph.valueline(data)) + .transition() + .duration(200) + .style("opacity", 1) + .each(b.setHook()) + .each("end", b.setNotifier(function() { + graph.pdata = data; + console.log("refresh complete"); + })); + console.log("refresh started"); + } + else + { + this.recalc_domain(pdata); + //console.log("shift: " + shift + "add: " + add); + data = this.pdata.concat(data.slice(data.length - add, data.length)); + for (var i = 0; i < data.length; i++) + data[i].x = i; + this.recalc_y_domain(data); + path.transition().duration(750) + .attr("d", graph.valueline(data)) + .transition() + .duration(750) + .attr("transform", "translate(" + this.x(-shift) + ")") + .each(b.setHook()) + .each("end", b.setNotifier(function() { + for (var i = 0; i < shift; i++) + data.shift(); + for (var i = 0; i < data.length; i++) + data[i].x = i; + graph.pdata = data; + d3.select(this).attr("d", graph.valueline(data)) + .attr("transform", "translate(" + graph.x(0) + ")"); + })); + } + svg.transition() + .duration(750) + .select(".x.axis") + .call(this.xAxis); + svg.transition() + .duration(750) + .select(".y.axis") + .call(this.yAxis); +} + +LineGraph.prototype.setup = function(elem, d, i, b) { + var graph = this; + var data = this.parse_data(d); + this.elem = elem; + var margin = {top: 10, right: 0, bottom: 50, left: 30}; + // Adds the svg canvas + var svg = d3.select(this.elem) + .append("svg") + .attr("width", "100%") + .attr("height", "100%") + .append("g") + .attr("transform", + "translate(" + margin.left + "," + margin.top + ")"); + var width = 500 - margin.left - margin.right; + var height = 250 - margin.top - margin.bottom; + console.log(width); + console.log(height); + // Set the ranges + this.x = d3.scale.linear().range([0, width]); + this.y = d3.scale.linear().range([height, 0]); + // Scale the range of the data + this.recalc_domain(data); + // Define the axes + this.xAxis = d3.svg.axis().scale(this.x) + .orient("bottom").ticks(5).tickFormat(d3.format("d")); + + this.yAxis = d3.svg.axis().scale(this.y) + .orient("left").ticks(5); + // Define the line + this.valueline = d3.svg.line() + .x(function(d) { return this.x(d.x); }) + .y(function(d) { return this.y(d.y); }); + /* + svg.append("text") + .attr("x", (width / 2)) + .attr("y", 0 - (margin.top / 2)) + .attr("text-anchor", "middle") + .style("font-size", "16px") + .style("text-decoration", "none") + .text(d.name); + */ + // Add the X Axis + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + height + ")") + .call(this.xAxis); + + // Add the Y Axis + svg.append("g") + .attr("class", "y axis") + .call(this.yAxis); + + svg.append("defs").append("clipPath") + .attr("id", "clip") + .append("rect") + .attr("width", width) + .attr("height", height); + + d3.select(this.elem).transition() + .duration(750) + .style("height", height + margin.top + margin.bottom + "px") + .each(b.setHook()) + .each("end", b.setNotifier(function() { + // Get the data + var path = svg.append("g") + .attr("clip-path", "url(#clip)") + .append("path"); + + path.attr("class", "line") + .style("stroke", random_color) + .attr("d", graph.valueline(data)); + var totalLength = path.node().getTotalLength(); + path.attr("stroke-dasharray", totalLength + " " + totalLength) + .attr("stroke-dashoffset", totalLength) + .transition() + .duration(2000) + .ease("linear") + .attr("stroke-dashoffset", 0) + .each(b.setHook()) + .each("end", b.setNotifier(function() { + path.attr("stroke-dasharray", ""); + })); + graph.pdata = data; + })); +} + +function ListGraph() {} + +ListGraph.prototype.parse_data = function(d) { + var data = []; + for (var i = 0; i < d.records.length; i++) + { + var rec = d.records[i]; + data.push({rid: rec[0], rec: rec}); + } + return data; +} + +ListGraph.prototype.setup = function(elem, d, i, b) { + var data = this.parse_data(d); + var div = d3.select(elem); + this.elem = elem; + /* + div.append("div") + .style("text-align", "center") + .append("a") + .style("font-size", "16px") + .style("text-decoration", "none") + .text(d.name); + */ + var table = d3.select(elem) + .append("table") + .attr("class", "listgraph table") + .style("width", "100%") + .style("height", "100%"); + this.table = table; + table.selectAll("tr").data(data) + .enter().append("tr") + .selectAll("td").data(function(d) { return d.rec; }) + .enter().append("td") + .style("opacity", 0) + .transition().duration(750) + .style("opacity", 1) + .text(function(d) { return d; }) + .each(b.setHook()) + .each("end", b.setNotifier(function(){})); + d3.select(elem) + //.transition().duration(750) + .style("height", null); +} + +ListGraph.prototype.update = function(d, i, b) { + var graph = this; + var data = this.parse_data(d); + var items = this.table.selectAll("tr").data(data, function(d) { return d.rid; }); + b.setHook()(); + var b0 = new Blocker(b.setNotifier(function() { + items.order(); + })); + + items.selectAll("td").text(function(d) { return d; }); + items.enter() + .append("tr") + .selectAll("td") + .data(function(d) { return d.rec; }) + .enter() + .append("td") + .text(function(d) { return d; }); + items.exit().remove(); + b0.go(); +} + +var graph_handler = {"linegraph": LineGraph, "listgraph": ListGraph}; + +function update_jobs(resp, after) { + resp = Object.keys(resp).map(function(k) { return resp[k] }); + var jobs = d3.select("#jobs").selectAll(".job_graph").data(resp, function(d) { return d.jid; }); + var b = new Blocker(after); + jobs.each(function(d, i) { + d3.select(this).select(".job_content").node().handler.update(d, i, b); + }); + var outer = jobs.enter() + .append("div") + .attr("class", "job_graph panel panel-info") + .style("width", "540px"); + outer.append("div") + .attr("class", "panel-heading") + .text(function(d) { return d.name; }); + outer.append("div") + .attr("class", "job_content panel-body") + .style("height", "0px") + .each(function(d, i) { + this.handler = new graph_handler[d.metadata.type](); + this.handler.setup(this, d, i, b); + }); + console.log(jobs.exit()); + jobs.exit() + .style("height", function(d) { return d3.select(this).style("height"); }) + .transition().duration(750) + .style("height", "0px") + .each(b.setHook()) + .each("end",b.setNotifier(function(){})) + .remove(); + b.go(); +} + +function graph_tick() { + send_request(function(resp) { + update_jobs(resp, function() { + setTimeout(graph_tick, refresh_rate); + }); + }); +} + +graph_tick(); -- cgit v1.2.3