Github user Crim commented on a diff in the pull request:

    https://github.com/apache/storm/pull/2129#discussion_r117833868
  
    --- Diff: storm-core/src/ui/public/js/visualization.js ---
    @@ -16,434 +16,330 @@
      * limitations under the License.
      */
     
    -// Inspired by
    -// 
https://github.com/samizdatco/arbor/blob/master/docs/sample-project/main.js
    -
    -function renderGraph(elem) {
    -
    -    var canvas = $(elem).get(0);
    -    canvas.width = $(window).width();
    -    canvas.height = $(window).height();
    -    var ctx = canvas.getContext("2d");
    -    var gfx = arbor.Graphics(canvas);
    -    var psys;
    -
    -    var totaltrans = 0;
    -    var weights = {};
    -    var texts = {};
    -    var update = false;
    -
    -    var myRenderer = {
    -        init: function(system){ 
    -            psys = system;
    -            psys.screenSize(canvas.width, canvas.height)
    -            psys.screenPadding(20);
    -            myRenderer.initMouseHandling();
    -        },
    -
    -        signal_update: function() {
    -            update = true;
    -        },
    -
    -        redraw: function() { 
    -            
    -            if(!psys)
    -                return;
    -            
    -            if(update) {
    -                totaltrans = calculate_total_transmitted(psys);
    -                weights = calculate_weights(psys, totaltrans);
    -                texts = calculate_texts(psys, totaltrans);
    -                update = false;
    +var _showSystem = false;
    +        var _showAcker = false;
    +        var _showMetrics = false;
    +        var container;
    +
    +        var options = {
    +            edges:{
    +                arrows: {
    +                    to:     {enabled: true, scaleFactor:1}
    +                },
    +                hoverWidth: 1.5,
    +                shadow:{
    +                    enabled: true,
    +                    color: 'rgba(0,0,0,0.5)',
    +                    size:10,
    +                    x:5,
    +                    y:5
    +                }
    +            },
    +            nodes: {
    +                color: {
    +                    border: '#2B7CE9',
    +                    background: '#97C2FC',
    +                    highlight: {
    +                        border: '#2B7CE9',
    +                        background: '#D2E5FF'
    +                    },
    +                    hover: {
    +                        border: '#2B7CE9',
    +                        background: '#D2E5FF'
    +                    }
    +                },
    +                shadow:{
    +                  enabled: true,
    +                  color: 'rgba(0,0,0,0.5)',
    +                  size:10,
    +                  x:5,
    +                  y:5
    +                },
    +            },
    +            physics:{
    +                enabled: false
    +            },
    +            layout: {
    +                randomSeed: 31337,
    +                improvedLayout:true,
    +                hierarchical: {
    +                    enabled: true,
    +                    levelSeparation: 150,
    +                    nodeSpacing: 300,
    +                    treeSpacing: 200,
    +                    blockShifting: true,
    +                    edgeMinimization: true,
    +                    parentCentralization: true,
    +                    direction: 'UD',        // UD, DU, LR, RL
    +                    sortMethod: 'directed'   // hubsize, directed
    +                }
    +            },
    +            interaction: {
    +                navigationButtons: false
                 }
    +        };
     
    +        // Holds all stream names
    +        var availableStreamsHash = { }
     
    +        // Holds our network
    +        var network;
     
    -            ctx.fillStyle = "white";
    -            ctx.fillRect(0, 0, canvas.width, canvas.height);
    -            var x = 0;
    -            
    +        // Holds nodes and edge definitions
    +        var nodes = new vis.DataSet();
    +        var edges = new vis.DataSet();
     
    -            psys.eachEdge(function(edge, pt1, pt2) {
    +        // Update/refresh settings
    +        var should_update = true
    +        var update_freq_ms = 30000
     
    -                var len = Math.sqrt(Math.pow(pt2.x - pt1.x,2) + 
Math.pow(pt2.y - pt1.y,2));
    -                var sublen = len - (Math.max(50, 20 + 
gfx.textWidth(edge.target.name)) / 2);
    -                var thirdlen = len/3;
    -                var theta = Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x);
    -                
    -                var newpt2 = {
    -                    x : pt1.x + (Math.cos(theta) * sublen),
    -                    y : pt1.y + (Math.sin(theta) * sublen)
    -                };
    +        function parseResponse(json) {
    +            console.log("Updating network");
     
    -                var thirdpt = {
    -                    x: pt1.x + (Math.cos(theta) * thirdlen),
    -                    y: pt1.y + (Math.sin(theta) * thirdlen)
    -                }
    +            // parse json
    +            for (var componentId in json) {
    +                parseNode(json[componentId], componentId);
    +            }
     
    -                weight = weights[edge.source.name + edge.target.name];
    -                
    -                if(!weights[edge.source.name + edge.target.name])
    -                {
    -                    totaltrans = calculate_total_transmitted(psys);
    -                    weights = calculate_weights(psys, totaltrans);
    -                }
    +            // Create network if it does not exist yet
    +            if (network == null) {
    +                createNetwork()
    +            }
    +        }
     
    -                ctx.strokeStyle = "rgba(0,0,0, .333)";
    -                ctx.lineWidth = 25 * weight + 5;
    -                ctx.beginPath();
    -
    -                var arrlen = 15;
    -                ctx.moveTo(pt1.x, pt1.y);
    -                ctx.lineTo(newpt2.x, newpt2.y);
    -                ctx.lineTo(newpt2.x - arrlen * Math.cos(theta-Math.PI/6), 
newpt2.y - arrlen * Math.sin(theta - Math.PI/6));
    -                ctx.moveTo(newpt2.x, newpt2.y);
    -                ctx.lineTo(newpt2.x - arrlen * Math.cos(theta+Math.PI/6), 
newpt2.y - arrlen * Math.sin(theta + Math.PI/6));
    -
    -                
    -                if (texts[edge.source.name + edge.target.name] == null)
    -                {
    -                    totaltrans = calculate_total_transmitted(psys);
    -                    texts = calculate_texts(psys, totaltrans);
    -                }
    +        function createNetwork() {
    +            var data = {
    +                nodes,
    +                edges
    +            };
    +            // Create network
    +            network = new vis.Network(container, data, options);
     
    -                gfx.text(texts[edge.source.name + edge.target.name], 
thirdpt.x, thirdpt.y + 10, {color:"black", align:"center", font:"Arial", 
size:10})
    -                ctx.stroke();
    +            // Create event handlers
    +            network.on("click", function (params) {
    +                handleClickEvent(params);
                 });
     
    -            psys.eachNode(function(node, pt) {
    -                var col;
    -
    -                var real_trans = gather_stream_count(node.data[":stats"], 
"default", "600");
    -                
    -                if(node.data[":type"] === "bolt") {
    -                    var cap = Math.min(node.data[":capacity"], 1);
    -                    var red = Math.floor(cap * 225) + 30;
    -                    var green = Math.floor(255 - red);
    -                    var blue = Math.floor(green/5);
    -                    col = arbor.colors.encode({r:red,g:green,b:blue,a:1});
    +            // Then disable layout
    +            network.setOptions({layout: {hierarchical: false } });
    +        }
    +
    +        function handleClickEvent(params) {
    +            // for debugging
    +            // console.log(JSON.stringify(params, null, 4));
    +            if (params["nodes"].length == 1) {
    +                handleNodeClickEvent(params["nodes"][0])
    +            }
    +        }
    +
    +        function handleNodeClickEvent(nodeId) {
    +            // Parse node event, populate node details
    +            console.log("Got event for " + nodeId);
    +
    +            // clear current values
    +            $("#bolt-details").empty();
    +
    +            // add new values
    +            var nodeDetails = json[nodeId];
    +            console.log(nodeDetails);
    +
    +            $("#bolt-details").append("<li>Id: <a href=\"" + 
nodeDetails[":link"] + "\" target=\"_new\">" + nodeId + "</a></li>");
    +            $("#bolt-details").append("<li>Type: " + nodeDetails[":type"] 
+ "</li>");
    +            $("#bolt-details").append("<li>Latency: " + 
nodeDetails[":latency"] + " ms</li>");
    +            $("#bolt-details").append("<li>Capacity: " + 
nodeDetails[":capacity"] + "</li>");
    +            $("#bolt-details").append("<li>Transferred: " + 
nodeDetails[":transferred"] + "</li>");
    +
    +            // List Inputs
    +            $("#bolt-details").append("<li><details><summary>" + 
nodeDetails[":inputs"].length + " Inputs</summary> <ul 
id=\"bolt-inputs\"></ul></details></li>");
    +            for (x=0; x<nodeDetails[":inputs"].length; x++) {
    +                var inputDetails = nodeDetails[":inputs"][x];
    +                var componentId = inputDetails[":component"];
    +                if (!componentId.startsWith("__")) {
    +                    $("#bolt-inputs").append("<li><details><summary><a 
href=\"#\" onclick=\"network.selectNodes(['" + componentId + "']); 
handleNodeClickEvent('" + componentId + "'); return false;\"> " + componentId + 
"</a></summary><ul id=\"bolt-inputs-" + x + "\"></ul></details></li>");
                     } else {
    -                    col = "#0000FF";
    +                    $("#bolt-inputs").append("<li><details><summary>" + 
componentId + "</summary><ul id=\"bolt-inputs-" + x + 
"\"></ul></details></li>");
                     }
    -                
    -                var w = Math.max(55, 25 + gfx.textWidth(node.name));
    -                
    -                gfx.oval(pt.x - w/2, pt.y - w/2, w, w, {fill: col});
    -                gfx.text(node.name, pt.x, pt.y+3, {color:"white", 
align:"center", font:"Arial", size:12});
    -                gfx.text(node.name, pt.x, pt.y+3, {color:"white", 
align:"center", font:"Arial", size:12});
    -                
    -                gfx.text(parseFloat(node.data[":latency"]).toFixed(2) + " 
ms", pt.x, pt.y + 17, {color:"white", align:"center", font:"Arial", size:12});
    -                
    -            });
    +                $("#bolt-inputs-" + x).append("<li>Stream: " + 
inputDetails[":stream"] + "</li>");
    +                $("#bolt-inputs-" + x).append("<li>Grouping: " + 
inputDetails[":grouping"] + "</li>");
    +            }
     
    -            // Draw gradient sidebar
    -            ctx.rect(0,0,50,canvas.height);
    -            var grd = ctx.createLinearGradient(0,0,50,canvas.height);
    -            grd.addColorStop(0, '#1ee12d');
    -            grd.addColorStop(1, '#ff0000');
    -            ctx.fillStyle=grd;
    -            ctx.fillRect(0,0,50,canvas.height);
    -            
    -            
    -        },
    -        
    -        initMouseHandling:function() {
    -            var dragged = null;
    -
    -            var clicked = false;
    -            
    -            var handler = {
    -                clicked:function(e){
    -                    var pos = $(canvas).offset();
    -                    _mouseP = arbor.Point(e.pageX-pos.left, e.pageY - 
pos.top);
    -                    dragged = psys.nearest(_mouseP);
    -                    
    -                    if(dragged && dragged.node !== null) {
    -                        dragged.node.fixed = true;
    -                    }
    -                    
    -                    clicked = true;
    -                    setTimeout(function(){clicked = false;}, 50);
    -
    -                    $(canvas).bind('mousemove', handler.dragged);
    -                    $(window).bind('mouseup', handler.dropped);
    -                    
    -                    return false;
    +            // Stats
    +            $("#bolt-details").append("<li><details><summary>" + 
nodeDetails[":stats"].length + " Instances</summary><ul 
id=\"bolt-instances\"></ul></details></li>");
    +            for (x=0; x<nodeDetails[":stats"].length; x++) {
    +                var statsDetails = nodeDetails[":stats"][x];
    +                $("#bolt-instances").append("<li><details><summary>" + 
statsDetails[":host"] + ":" + statsDetails[":port"] + "</summary><ul 
id=\"bolt-instances-" + x + "\"></ul></details></li>");
    +                $("#bolt-instances-" + x).append("<li>Up: " + 
secondsToString(statsDetails[":uptime_secs"]) + "</li>");
    +            }
    +
    +            $("#bolt_slideout_inner").css("display", "inline");
    +
    +            return;
    +        }
    +
    +        function parseNode(nodeJson, nodeId) {
    +            console.log("parsing node " + nodeId);
    +            // Conditionally hide certain nodes
    +            if (!showNode(nodeId)) {
    +                return
    +            }
    +
    +            // Determine node color
    +            var col = "#97C2FC"
    +            var shape = "dot"
    +            if (nodeJson[":type"] === "bolt") {
    +                // Determine color based on capacity
    +                var cap = Math.min(nodeJson[":capacity"], 1);
    +                var red = Math.floor(cap * 225) + 30;
    +                var green = Math.floor(255 - red);
    +                var blue = Math.floor(green/5);
    +                col = "rgba(" + red + "," + green + "," + blue + ",1)"
    +            }
    +            if (nodeJson[":type"] === "spout") {
    +                shape = "triangleDown";
    +            }
    +
    +            // Generate title
    +            var title = "<b>" + nodeId + "</b><br/>";
    +            title += "Capacity: " + nodeJson[":capacity"] + "<br/>";
    +            title += "Latency: " + nodeJson[":latency"]
    +
    +            // Construct the node
    +            var node = {
    +                "id": nodeId,
    +                "label": nodeId,
    +                "color": {
    +                    "background": col
                     },
    -                
    -                dragged:function(e) {
    -
    -                    var pos = $(canvas).offset();
    -                    var s = arbor.Point(e.pageX-pos.left, e.pageY-pos.top);
    -                    
    -                    if(dragged && dragged.node != null) {
    -                        var p = psys.fromScreen(s);
    -                        dragged.node.p = p;
    -                    }
    -                    
    -                    return false;
    -                    
    +                "shape": shape,
    +                "shadow": {
    +                    "enabled": true
                     },
    +                "title": title,
    +                "size": 45
    +            };
     
    -                dropped:function(e) {
    -                    if(clicked) {
    -                        if(dragged.distance < 50) {
    -                            if(dragged && dragged.node != null) { 
    -                                window.location = 
dragged.node.data[":link"];
    -                            }
    -                        }
    -                    }
    +            // Construct edges
    +            for (var index in nodeJson[":inputs"]) {
    +                var inputComponent = nodeJson[":inputs"][index];
    +                parseEdge(inputComponent, nodeId);
    +            }
     
    -                    if(dragged === null || dragged.node === undefined) 
return;
    -                    if(dragged.node !== null) dragged.node.fixed = false;
    -                    dragged.node.tempMass = 1000;
    -                    dragged = null;
    -                    $(canvas).unbind('mousemove', handler.dragged);
    -                    $(window).unbind('mouseup', handler.dropped);
    -                    _mouseP = null;
    -                    return false;
    -                }
    -                
    +            if (node != null) {
    +                nodes.update(node);
                 }
    -            
    -            $(canvas).mousedown(handler.clicked);
             }
    -    }
    -    
    -    return myRenderer;
    -}
    -
    -function calculate_texts(psys, totaltrans) {
    -    var texts = {};
    -    psys.eachEdge(function(edge, pt1, pt2) {
    -        var text = "";
    -        for(var i = 0; i < edge.target.data[":inputs"].length; i++) {
    -            var stream = edge.target.data[":inputs"][i][":stream"];
    -            var sani_stream = 
edge.target.data[":inputs"][i][":sani-stream"];
    -            if(stream_checked(sani_stream) 
    -               && edge.target.data[":inputs"][i][":component"] === 
edge.source.name) {
    -                stream_transfered = 
gather_stream_count(edge.source.data[":stats"], sani_stream, "600");
    -                text += stream + ": " 
    -                    + stream_transfered + ": " 
    -                    + (totaltrans > 0  ? 
Math.round((stream_transfered/totaltrans) * 100) : 0) + "%\n";
    -                
    +
    +        function showNode(nodeId) {
    +            if (nodeId == "__system" && !_showSystem) {
    +                return false;
                 }
    -        }
    -        
    -        texts[edge.source.name + edge.target.name] = text;
    -    });
    -
    -    return texts;
    -}
    -
    -function calculate_weights(psys, totaltrans) {
    -    var weights = {};
    - 
    -    psys.eachEdge(function(edge, pt1, pt2) {
    -        var trans = 0;
    -        for(var i = 0; i < edge.target.data[":inputs"].length; i++) {
    -            var stream = edge.target.data[":inputs"][i][":sani-stream"];
    -            if(stream_checked(stream) && 
edge.target.data[":inputs"][i][":component"] === edge.source.name)
    -                trans += gather_stream_count(edge.source.data[":stats"], 
stream, "600");
    -        }
    -        weights[edge.source.name + edge.target.name] = (totaltrans > 0 ? 
trans/totaltrans : 0);
    -    });
    -    return weights;
    -}
    -
    -function calculate_total_transmitted(psys) {
    -    var totaltrans = 0;
    -    var countedmap = {}
    -    psys.eachEdge(function(node, pt, pt2) {
    -        if(!countedmap[node.source.name])
    -            countedmap[node.source.name] = {};
    -
    -        for(var i = 0; i < node.target.data[":inputs"].length; i++) {
    -            var stream = node.target.data[":inputs"][i][":stream"];
    -            
if(stream_checked(node.target.data[":inputs"][i][":sani-stream"]))
    -            {
    -                if(!countedmap[node.source.name][stream]) {
    -                    if(node.source.data[":stats"])
    -                    {
    -                        var toadd = 
gather_stream_count(node.source.data[":stats"], 
node.target.data[":inputs"][i][":sani-stream"], "600");
    -                        totaltrans += toadd;
    -                    }
    -                    countedmap[node.source.name][stream] = true;
    -                }
    +            if (nodeId == "__acker" && !_showAcker) {
    +                return false
    +            }
    +            if (nodeId == "__metrics" && !__showMetrics) {
    +                return false
                 }
    +            return true
             }
    -        
    -    });
    -
    -    return totaltrans;
    -}
    -
    -function has_checked_stream_input(inputs) {
    -    
    -    for(var i = 0; i < inputs.length; i++) {
    -        var x = stream_checked(inputs[i][":sani-stream"]);
    -        if(x) 
    -            return true;
    -    }
    -    return false;
    -}
    -
    -function stream_checked(stream) {
    -    var checked = $("#" + stream).is(":checked");
    -    return checked;
    -}
    -
    -function has_checked_stream_output(jdat, component) {
    -    var ret = false;
    -    $.each(jdat, function(k, v) {
    -        for(var i = 0; i < v[":inputs"].length; i++) {
    -            if(stream_checked(v[":inputs"][i][":sani-stream"]) 
    -               && v[":inputs"][i][":component"] == component)
    -                ret = true;
    +
    +        function parseEdge(edgeJson, sourceId) {
    +            // Make this stream available
    +            addAvailableStream(edgeJson[":stream"])
    +
    +            // Create a unique Id
    +            var id = edgeJson[":component"] + ":" + sourceId + ":" + 
edgeJson[":stream"];
    +
    +            // Determine if stream is enabled
    +            if (!isStreamEnabled(edgeJson[":stream"])) {
    +                // Remove edge
    +                edges.remove({ "id": id });
    +                return
    +            }
    +
    +            if (!edges.get(id)) {
    +                console.log("Updating edge " + id );
    +                edges.update({
    +                    "id": id,
    +                    "from": edgeJson[":component"],
    +                    "to": sourceId,
    +                    "label": edgeJson[":stream"],
    +                    "title": "From: " + edgeJson[":component"] + "<br>To: 
" + sourceId + "<br>Grouping: " + edgeJson[":grouping"]
    +                });
    +            }
             }
    -    });
    -    return ret;
    -}
    -
    -function gather_stream_count(stats, stream, time) {
    -    var transferred = 0;
    -    if(stats)
    -        for(var i = 0; i < stats.length; i++) {
    -            if(stats[i][":transferred"] != null && 
stats[i][":transferred"][time] != undefined)
    -            {
    -                var stream_trans = stats[i][":transferred"][time][stream];
    -                if(stream_trans != null)
    -                    transferred += stream_trans;
    +
    +        function addAvailableStream(streamId) {
    +            // Create a master list of all streams
    +            if (availableStreamsHash[streamId] == null) {
    +                availableStreamsHash[streamId] = true;
    +                updateAvailableStreams();
                 }
             }
    -    return transferred;
    -}
    -
    -
    -function rechoose(jdat, sys, box) {
    -    var id = box.id;
    -    if($(box).is(':checked'))
    -    {
    -        //Check each node in our json data to see if it has inputs from or 
outputs to selected streams. If it does, add a node for it.
    -        $.each(jdat,function(k,v) {
    -            if( has_checked_stream_input(v[":inputs"]) || 
has_checked_stream_output(jdat, k))
    -                sys.addNode(k,v);
    -        });
    -           
    -        //Check each node in our json data and add necessary edges based 
on selected components.
    -        $.each(jdat, function(k, v) {
    -            for(var i = 0; i < v[":inputs"].length; i++)
    -                if(v[":inputs"][i][":sani-stream"] === id) {
    -                    
    -                    sys.addEdge(v[":inputs"][i][":component"], k, v);
    +
    +        function updateAvailableStreams() {
    +            var container = jQuery("#available-streams");
    +            for (var streamName in availableStreamsHash) {
    +                var entry = jQuery(container).find("#stream-" + streamName)
    --- End diff --
    
    Ah I've never played with Trident, I'll dig into that and see what is going 
on and sanitize them where needed.  Thanks for the heads up. 👍 
    
    I'll also dig into making the sizing of the slide outs dynamic.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastruct...@apache.org or file a JIRA ticket
with INFRA.
---

Reply via email to