Wednesday, November 6, 2013

The Most Useful Dialog I've Ever Built

For many years, I've been lucky to work on some very cool, interesting, fun, and important projects.  Sometimes, when you are fully immersed in the technology you are building, you don't always see the big picture.  Later on, you might be able to look back and fully grasp what you designed, what you built and how someone might actually do something useful with it.

I thought that my very first project, coming out of college and joining Digital Equipment (DEC), was the coolest thing.  It was called DECplan, a project planning and management system.  Remember Gantt Charts?  Precedence Diagrams?  Project Schedules?   I was tasked with "reports".  That's it.  Build a way for users to get reports out of the system.  I designed and built the Report Layout Editor.  A way to take a blank sheet of paper and customize a report.  Basically, drag database fields onto the page and then "generate" the report.  Looking back, our group built Microsoft Project before there was even a Microsoft.  I had no idea that I built a Microsoft Word like interface with one of the first graphical toolkits in the industry....DECwindows.  Very useful.


Next stop, Wellfleet.  A small networking company that had grand plans of doing big things... and it did.  As it grew, to Bay Networks and eventually Nortel Networks, I was lucky to be on the team that was tasked with managing *ALL* the networking products....routers, switches, hubs, and on.  Our lab was huge, with one of every product in the companies offering.  The interface (now using SUN equipment with Motif) had to show all the different devices, each with unique settings, fields and options, in a generic way that allowed for ease of use.  This was a challenge and a success as it did display all the equipment it discovered in a single cohesive view.  Very useful.

Virtualization was born.  An industry that has spawned numerous companies and has provided thousands of jobs to this day.  Now using Java and Swing and being able to really build not only a useful application, but something that was pleasing to use and visually intriguing, was the next challenge.  I was lucky again, to land in the fast paced startup world in the middle of an emerging technology company, Virtual Iron.  Design and build an application for managing virtual machines.  Show me what's running, allow me to rollout new machines quickly and put it all at my finger tips.  Very useful.



Along came SDN.  To be fair, SDN occurred a few years after I had already started at my next lucky stop, Plexxi Inc.  Long before network overlays, SDN, DevOps integration, and all the present day technologies started coming together, Plexxi was already working on it.

Design and build an application that shows me application conversations in my network, lets me put policies on those conversations, automatically adjust my network and show me the results.  Wow.  Of course this would be the most useful application I've ever built.   Then, one day, well into the development of Plexxi Control, the subject of DevOps and troubleshooting and network management came up.  I need to find something in my network, but I don't know much about what I'm looking for.  Maybe I know the IP Address, maybe a name, maybe a type of object, maybe a MAC Address.  Help!

And there it was, "The Most Useful Dialog I've Ever Built."



Can you find this for me?  Do you have this object in your controller database?  What's the name? Do you know the MAC or IP address?  What operations can I do to it?  Which Plexxi Switch sees this object?  What port is it seen on?  Is it on a VLAN? Is it Affinitized? 

It may seem simple, but being able to access and display this kind of information from our central controller helps answer ( in a single press of a "Find" button) a very complex question, that typically involves querying several applications, people, spreadsheets, emails, and all sorts of disorganized network information.  Very very useful.




Thursday, June 27, 2013

Show Me An Affinity

- Simplifying Complex Processing with a User Interface -

Every application I have ever worked on had the same end goal; provide useful, simple, easy to use and visually appealing screens.  Don't confuse me.  Don't make me click hundreds of times.  Don't leave important information out.  If something fails, tell me what to do next.

In designing a set of views and screens for my current project, the problem statement can be summarized as follows:
  • Show me where my applications are in my network
  • Let me pick the important applications and designate them as such
  • Let me define how these applications should be conversing
  • Do some SDN magic on these applications
  • Show me where my applications are in my ring
Providing the ability to see and do all of these actions requires a full application suite where common information is displayed from different perspectives.

In my Network view, I can search for specific virtual machines, among other things, and put them into an affinity group, thereby capturing the importance of an application in my network.





I am interested in how my three applications:
  • authhost71
  • authbox81
  • authclient62
converse with their backend databases dbserver28 and dbserver84. 



Everything in App1 talks with DB1, bidirectionally,  and I'd really like these conversations to be isolated from other ring traffic.

Is my ring ready for the SDN magic fitting algorithm to do it's thing?    Looks formed, healthy, good to go.



For each of my conversations, where are my applications traversing the ring?  My fitting results shows me how each application in App1 talks with each application in DB1; where it enters my ring, how it traverses my ring and where it exits my ring.





I found some applications in my network and after affinitizing them ("is that word? affinitizing?  it will be soon...."), I see clearly how my ring is handling their conversations and isolating them from all others. 

Taking a complex set of data and simplifying the ability to work with that data is what a good user interface is all about.   Show me an affinity? I saw an affinity today.





Friday, March 15, 2013

Fun with JIT, part2

Here's some more neat things I've done with my GWT JIT integration.  The code snippets here are to represent what is possible and your mileage may vary.

Dragging Objects

In many of the JIT renderings, you can drag objects around the screen and can have full control over what you allow to be dragged.  You start by getting a callback when dragging is occurring:

onDragMove : function(node, eventInfo, e) {

where the "node" is the object that is being dragged and "eventInfo" has position information.  For a Force Directed JIT graph type, to allow dragging of your objects, you simply do this inside the onDragMove function:

var pos = eventInfo.getPos();
node.pos.setc(pos.x, pos.y);
fd.plot();

By the way, "fd" is an instance of the Force Directed graph.

As you drag the object around the screen, it will be moved and the graph will be replotted to show the update.  If the object you are dragging has any edges attached to it, they will automatically be moved as well.  But what if you have relationships on your display?  What if you have a parent object that has several children connected to it and when you drag the parent, you want the children to be dragged as well.  At Plexxi, we display a RING object with Plexxi Switches connected to the ring.  If a user drags our ring object, we want the switches to go with it.  Here's how:

onDragMove : function(node, eventInfo, e) {
    var pos = eventInfo.getPos();
    if (node.getData('type') == "plexxiring") {
        node.eachAdjacency(function(a) {
            childnode = fd.graph.getNode(a.nodeTo.id);
            childnode.pos.setc(childnode.getPos().x - (node.getPos().x - pos.x),
                                          childnode.getPos().y - (node.getPos().y - pos.y));
        });

    node.pos.setc(pos.x, pos.y);
    fd.plot();

What we do is get all the nodes connected to our dragged node and move them as well.  This performs surprisingly well in our application.  You can take this further and re-curse through all your objects moving even more when a single one is dragged.

Mouseover Tool Tips

On some occasions, you may want a tooltip or a text box to show up on the screen as the user moves the mouse over your nodes.  To insure good performance, you will want to add the tooltip text to a private JSON data object so that this text is instantly available to you at any time.  If you have to go back to your database of make some other calls to get your tooltip text, you may suffer from bad performance in rendering them. 

Here's how I did Tips with my Force Directed graph:

Tips : {
    enable : true,
    type : 'Native',
    offsetX : 10,
    offsetY : 10,
    onShow : function(tip, node) {
        tip.innerHTML = node.getData('tooltip');
   },


Auto Refreshing

One of the biggest issues with any graphical application is refreshing.  Does your user have to press a refresh button?  Does that waste time retrieving data from your database, destroying whatever you are currently displaying and rebuilding the display with new data? 

By using GWT and the built-in RPC mechanism, I am able to ask my GWT Server if any data has changed and then act of it.  This is a topic for another blog....but let's say my Force Directed graph is showing 5 different types of objects on it.  I ask the GWT Server to coordinate with my backend database to keep a cache of the objects I'm displaying and if any of these objects is modified, or deleted, let me know.  When I find out something that I am displaying has changed, then I take action.  The most common events are creating or deleting a link (edge), deleting an object or changing the name of an object.  Here's how they are handled as called by my GWT code:

private native void createLink(JavaScriptObject nativeGraph, String link, String from, String to)/*-{
        if (nativeGraph.graph.getAdjacence(from, to) == null) {
            var fromNode = nativeGraph.graph.getNode(from);
            var toNode = nativeGraph.graph.getNode(to);
            var adj = nativeGraph.graph.addAdjacence(fromNode, toNode,
                            {
                                'linkUuid' : link,
                                'selected' : 'false',
                                'linkcolor' : '#FF0000'
                            });
              
        nativeGraph.plot();
    }
}-*/;

private native void deleteLink(JavaScriptObject nativeGraph, String linkUuid)/*-{
        nativeGraph.graph.eachNode(function(n) {
            n.eachAdjacency(function(a) {
                if (a.data.linkUuid == linkUuid) {
                    a.setData('alpha', '0');
                    nativeGraph.graph.removeAdjacence(a.nodeFrom.id,
                            a.nodeTo.id);

                    nativeGraph.plot();
                    return;
                }
            });
        });
    }-*/;

private native void deleteNode(JavaScriptObject nativeGraph, String nodeUuid)/*-{
        nativeGraph.graph.eachNode(function(n) {
            if (n.id == nodeUuid) {
                n.setData('alpha', 0, 'end');
               nativeGraph.removeNode(nodeUuid, {
                     type: 'fade:seq'
                    duration: 1000, 
                    hideLabels: false 
                    transition: $jit.Trans.Quart.easeOut   
               });
            }
        });
 }-*/;


private native void updateNodeName(JavaScriptObject nativeGraph, String uuid, String newName)/*-{
        var node = nativeGraph.graph.getNode(uuid);
        if (node != null) {
            node.name = newName;
            nativeGraph.plot();
        }
}-*/;

fun with JIT part3 might be in the works......enjoy.





Friday, January 18, 2013

Fun with JIT, part1

Over the past year, I've been integrating JIT  (thejit.org) with my GWT application.  Javascript is not something for the faint of heart and personally I try to stay far away from it instead allowing  GWT to generate the Javascript for me based on my Java implementation code.  But to make use of, extend, and better control the canvas based layout tool JIT, I had to take on some Javascript.  So here's some things I did with it.  Any code snippets here should be considered pseudo-code and are for representation only.


Saving & Restoring Node Positions

The Force-Directed Graph has a built in layout algorithm that draws a graph as best as it can.  However, each time the graph is rendered, the layout algorithm produces a different layout.  We offer the user the ability to control the layout somewhat, specifying spacing between nodes and how many iterations the algorithm should use based on a complexity setting and some others:


Once the layout has been run, we then let the user drag the nodes and links around the screen to better position items to their liking.  To fully make this work, we need to save the node positions so that the next time we view the graph, we put all the nodes back into their original positions.  This also provides a big performance boost, in that we never run the layout algorithm a second time, but instead tell JIT where we want the nodes.

Save Positions:

// We store the node positions using our string object id, and the x,y values.
HashMap<String, ArrayList<Integer>> nodePositions;

// We iterate over the graph, getting the positions of each node.
mygraph.graph.eachNode(function(n) {
saveNodeData(n.id, Math.floor(n.getPos().x), Math.floor(n.getPos().y));
});

saveNodeData(String id, int x, int y)
{
    ArrayList<Integer> locations = new ArrayList<Integer>();
    locations.add(x);
    locations.add(y);
    nodePositions.put(id, locations);
}

Restore Positions:

// Lood our JSON data
nativeGraph.loadJSON(data);

// Put the nodes at the positions we want. No need to run the layout algorithm.

iterate over the saved node positions data and call this:
setNodePosition(JavaScriptObject nativeGraph, String id, int x, int y)
{
    node = nativeGraph.graph.getNode(id);
    if (node != null) {
        node.setPos(new $wnd.$jit.Complex(x,y), 'current');
        nativeGraph.plot();
    }
}


Resizing the Graph

JIT comes with some built-in functionality on many of their graphs that allows the user to scroll their mouse wheel which then causes the canvas to scale in and out, making the drawn objects grow or shrink.  I found this functionality to be somewhat difficult to control and also I wanted to control how large and how small I would allow the screen to scale to.  



I implemented two push buttons on my graph that allow the user to scale up or down as they are pressed.  One jumps 125% and the other jumps 80% which provides an even up/down scaling so that they user can always return to the default, 100%.

Scaling In:

Scaling in makes the objects larger.  Each time the user presses the "+" button, I ask JIT to:

nativeGraph.canvas.scale(1.25, 1.25);


Scaling Out:

Scaling out makes the objects smaller.  Each time the user presses the "-" button, I ask JIT to:

nativeGraph.canvas.scale(.8, .8);


Searching the Graph

Since my graph can get quite large with many of the objects off the edges of the viewable area, I implemented the ability to search for any node by name.  Once found, the node would be selected and the graph would be centered on the object, bringing the object into view for the user.  Here's how.


Find Node:

findNode(JavaScriptObject nativeGraph, String name)
{
    var p = new RegExp(name.replace("*", ".*"));
    
    nativeGraph.graph.eachNode(function(node) {
        if (p.test(node.name)) {
            // Found the node, we can get the x,y positions of this node.
    centerScreen(node.getPos().x, node.getPos().y);
}
}

centerScreen(String x, String y)
{
    // We are using a scroll panel with scroll bars as the parent of our JIT canvas
    // so we just adjust these scrollbars.
    graphContainer.setScrollLeft(((graphWidth / 2) -
(getOffsetWidth() / 2)) + x);
    graphContainer.setScrollTop(((graphHeight / 2) -
(getOffsetHeight() / 2) + y);
}

...More Fun with JIT, part2 coming soon.