aboutsummaryrefslogblamecommitdiff
path: root/assets/js/monitor.js
blob: a92f75349e82d7112230ade86934a3dd124fea90 (plain) (tree)































































































































































































































































































































































                                                                                                    
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();