diff options
Diffstat (limited to 'static/assets/js/monitor.js')
-rw-r--r-- | static/assets/js/monitor.js | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/static/assets/js/monitor.js b/static/assets/js/monitor.js new file mode 100644 index 0000000..0aa0e5a --- /dev/null +++ b/static/assets/js/monitor.js @@ -0,0 +1,394 @@ +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) + 150) / 2; + var g = (random_range(0, 200) + 150) / 2; + var b = (random_range(0, 200) + 150) / 2; + return "#" + hex(r) + hex(g) + hex(b); +} + +function LineGraph() {} + +LineGraph.prototype.recalc_y_domain = function(mdata) { + var min = d3.min($.map(mdata, function (data) { + return d3.min(data, function(d) { return d.y; }); })); + var max = d3.max($.map(mdata, function (data) { + return d3.max(data, function(d) { return d.y; }); })); + var delta = max - min; + this.y.domain([min - Math.max(delta / 3.0, 1), max + Math.max(delta / 3.0, 1)]); +} + +LineGraph.prototype.recalc_x_domain = function(mdata) { + this.x.domain(d3.extent(mdata[0], function(d) { return d.x; })); +} + +LineGraph.prototype.recalc_domain = function(mdata) { + this.recalc_x_domain(mdata); + this.recalc_y_domain(mdata); +} + +LineGraph.prototype.parse_data = function(d) { + var data = []; + for (var i = 0; i < d.records.length; i++) + { + var row = d.records[i]; + while (data.length < row.rec.length) + data.push([]); + for (var j = 0; j < row.rec.length; j++) + data[j].push({x: i, y: +row.rec[j], rid: row.rid}); + } + return data; +} + +LineGraph.prototype.update = function(d, i, b) { + var graph = this; + var mdata = this.parse_data(d); + if (mdata.length == 0) return; + var recalced = false; + var svg = d3.select(this.elem).select("svg"); + this.recalc_y_domain(mdata); + var ya = svg.select(".y.axis"); + ya.transition() + .duration(750) + .call(this.yAxis); + var maxw = ya.node().getBBox().width; + svg.select("g") + .attr("transform", "translate(" + (maxw + this.margin.left) + "," + this.margin.top + ")"); + var niw = this.width - maxw; + if (Math.abs(niw - this.inner_width) > 1e-6) + { + this.inner_width = niw; + this.x = d3.scale.linear().range([0, this.inner_width]); + this.xAxis = d3.svg.axis().scale(this.x) + .orient("bottom").ticks(5).tickFormat(d3.format("d")); + } + this.recalc_x_domain(this.pdata); + svg.transition() + .duration(750) + .select(".x.axis") + .call(this.xAxis); + svg.select("rect") + .attr("width", this.inner_width); + for (var i = 0; i < mdata.length; i++) + { + var data = mdata[i]; + if (data.length == 0) continue; + var pdata = this.pdata[i]; + var path = this.line[i]; + + 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"); + } + if (!recalced) + { + this.recalc_domain(mdata); + recalced = true; + } + 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(i, data) { return function() { + graph.pdata[i] = data; + console.log("refresh complete"); + }}(i, data))); + console.log("refresh started"); + } + else + { + if (!recalced) + { + this.recalc_domain(this.pdata); + recalced = true; + } + //console.log("shift: " + shift + "add: " + add); + data = pdata.concat(data.slice(data.length - add, data.length)); + for (var j = 0; j < data.length; j++) + data[j].x = j; + this.recalc_y_domain(mdata); + 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(i, data) { return function() { + for (var j = 0; j < shift; j++) + data.shift(); + for (var j = 0; j < data.length; j++) + data[j].x = j; + graph.pdata[i] = data; + d3.select(this).attr("d", graph.valueline(data)) + .attr("transform", "translate(" + graph.x(0) + ")"); + }}(i, data))); + } + } +} + +LineGraph.prototype.setup = function(elem, d, i, b) { + var graph = this; + this.elem = elem; + var margin = this.margin = {top: 10, right: 0, bottom: 50, left: 0}; + var clippath_name = "clip" + d.jid; + // 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 + ")"); + this.width = 500 - margin.left - margin.right; + this.height = 250 - margin.top - margin.bottom; + var mdata = this.parse_data(d); + + this.y = d3.scale.linear().range([this.height, 0]); + if (mdata.length) this.recalc_y_domain(mdata); + this.yAxis = d3.svg.axis().scale(this.y) + .orient("left").ticks(5); + + // Add the Y Axis + var ya = svg.append("g") + .attr("class", "y axis") + .call(this.yAxis); + /* + ya.selectAll("text").each(function() { + if(this.getBBox().width > maxw) maxw = this.getBBox().width; + }); + */ + var maxw = ya.node().getBBox().width; + svg.attr("transform", "translate(" + (maxw + margin.left) + "," + margin.top + ")"); + this.inner_width = this.width - maxw; + this.x = d3.scale.linear().range([0, this.inner_width]); + if (mdata.length) this.recalc_x_domain(mdata); + this.xAxis = d3.svg.axis().scale(this.x) + .orient("bottom").ticks(5).tickFormat(d3.format("d")); + // Define the line + this.valueline = d3.svg.line() + .x(function(d) { return this.x(d.x); }) + .y(function(d) { return this.y(d.y); }); + // Add the X Axis + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + this.height + ")") + .call(this.xAxis); + + svg.append("defs").append("clipPath") + .attr("id", clippath_name) + .append("rect") + .attr("width", this.inner_width) + .attr("height", this.height); + + this.pdata = []; + this.line = []; + fdata = []; + fpath = []; + d3.select(this.elem).transition() + .duration(750) + .style("height", this.height + this.margin.top + this.margin.bottom + "px") + .each(b.setHook()) + .each("end", b.setNotifier(function() { + // Get the data + for (var i = 0; i < mdata.length; i++) + { + var data = mdata[i]; + var path = svg.append("g") + .attr("clip-path", "url(#" + clippath_name + ")") + .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(data, path) { return function() { + path.attr("stroke-dasharray", ""); + graph.line.push(path); + }}(data, path))); + } + graph.pdata = mdata; + })); +} + +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); + }); + 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(); |