Entity graph hightlight and pin-all
Change-Id: Ia9e653c221243cd0d28cfc1e9ae4c5d6718a6766
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<hz-entities-info item="vm.selectedItem"></hz-entities-info>
|
<hz-entities-info item="vm.selectedItem"></hz-entities-info>
|
||||||
|
<hz-entities-toolbox></hz-entities-toolbox>
|
||||||
<hz-entities-graph data="vm.graphData" selected="vm.model.selected" item-selected="vm.setSelected"></hz-entities-graph>
|
<hz-entities-graph data="vm.graphData" selected="vm.model.selected" item-selected="vm.setSelected"></hz-entities-graph>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
.entities {
|
.entities {
|
||||||
.panel-body {
|
.panel-body {
|
||||||
padding: 3px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,14 +24,34 @@ function hzEntitiesGraph() {
|
|||||||
linkWidth = 1,
|
linkWidth = 1,
|
||||||
circleRadius = 14,
|
circleRadius = 14,
|
||||||
circlePadding = 1,
|
circlePadding = 1,
|
||||||
pinned = horizon.cookies.get('pinned') || [],
|
|
||||||
zoom = d3.behavior.zoom().scaleExtent([minZoom, maxZoom]),
|
zoom = d3.behavior.zoom().scaleExtent([minZoom, maxZoom]),
|
||||||
ellipsisWidth = 80,
|
ellipsisWidth = 80,
|
||||||
|
hightlightDepth = 2,
|
||||||
|
heightOffset,
|
||||||
|
pinned,
|
||||||
graphCreated,
|
graphCreated,
|
||||||
node,
|
node,
|
||||||
link,
|
link,
|
||||||
|
linksMap,
|
||||||
content;
|
content;
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var p = $('.panel.panel-primary');
|
||||||
|
heightOffset = (p.length ? p.offset().top : 180) + 75;
|
||||||
|
|
||||||
|
pinned = horizon.cookies.get('pinned') || [];
|
||||||
|
if (_.isString(pinned)) {
|
||||||
|
try {
|
||||||
|
pinned = JSON.parse(pinned);
|
||||||
|
}
|
||||||
|
catch(ex) {
|
||||||
|
pinned = [];
|
||||||
|
console.error('Failed to parse the pinned cookie');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
scope.$watch('data.ts', function(newVal, oldVal) {
|
scope.$watch('data.ts', function(newVal, oldVal) {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
prepareData();
|
prepareData();
|
||||||
@@ -44,6 +64,22 @@ function hzEntitiesGraph() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
scope.$on('toolbox-pin', function () {
|
||||||
|
pinAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$on('toolbox-unpin', function () {
|
||||||
|
unpinAll();
|
||||||
|
})
|
||||||
|
|
||||||
|
scope.$on('toolbox-zoom-to-fit', function () {
|
||||||
|
console.log('on toolbox-pin', arguments)
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$on('toolbox-toggle-fullscreen', function () {
|
||||||
|
console.log('on toolbox-unpin', arguments)
|
||||||
|
})
|
||||||
|
|
||||||
scope.isEmpty = function() {
|
scope.isEmpty = function() {
|
||||||
return scope.data && scope.data.nodes && scope.data.nodes.length === 0;
|
return scope.data && scope.data.nodes && scope.data.nodes.length === 0;
|
||||||
};
|
};
|
||||||
@@ -90,7 +126,7 @@ function hzEntitiesGraph() {
|
|||||||
d3.select(window).on('resize', resize);
|
d3.select(window).on('resize', resize);
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
svg.attr('height', window.innerHeight - 168 + 'px')
|
svg.attr('height', window.innerHeight - heightOffset + 'px')
|
||||||
force.size([angular.element(svg[0]).width(),
|
force.size([angular.element(svg[0]).width(),
|
||||||
angular.element(svg[0]).height()])
|
angular.element(svg[0]).height()])
|
||||||
.resume();
|
.resume();
|
||||||
@@ -108,6 +144,11 @@ function hzEntitiesGraph() {
|
|||||||
node.y = pin.y;
|
node.y = pin.y;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
linksMap = {};
|
||||||
|
_.each(scope.data.links, function(link) {
|
||||||
|
linksMap[link.source.id + ',' + link.target.id] = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createGraph() {
|
function createGraph() {
|
||||||
@@ -228,6 +269,7 @@ function hzEntitiesGraph() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.drawGraph = drawGraph;
|
window.drawGraph = drawGraph;
|
||||||
|
window.dforce = force;
|
||||||
|
|
||||||
function drawGraph() {
|
function drawGraph() {
|
||||||
link = link.data(force.links(), function(d) { return d.source.id + '-' + d.target.id; });
|
link = link.data(force.links(), function(d) { return d.source.id + '-' + d.target.id; });
|
||||||
@@ -275,28 +317,47 @@ function hzEntitiesGraph() {
|
|||||||
.attr('dominant-baseline', 'central')
|
.attr('dominant-baseline', 'central')
|
||||||
.attr('transform', 'scale(1)')
|
.attr('transform', 'scale(1)')
|
||||||
.attr('class', function(d) {
|
.attr('class', function(d) {
|
||||||
var cls = '';
|
var category = d.category,
|
||||||
var severity = d.operational_severity;
|
cls = '';
|
||||||
if (severity) {
|
|
||||||
switch (severity.toLowerCase()) {
|
if (category && category.toLowerCase() === 'alarm') {
|
||||||
case 'critical':
|
var severity = d.operational_severity;
|
||||||
cls = 'red';
|
if (severity) {
|
||||||
break;
|
switch (severity.toLowerCase()) {
|
||||||
case 'severe':
|
case 'critical':
|
||||||
cls = 'orange';
|
cls = 'red';
|
||||||
break;
|
break;
|
||||||
case 'warning':
|
case 'severe':
|
||||||
cls = 'yellow';
|
cls = 'orange';
|
||||||
break;
|
break;
|
||||||
case 'ok':
|
case 'warning':
|
||||||
cls = 'green';
|
cls = 'yellow';
|
||||||
break;
|
break;
|
||||||
case 'n/a':
|
case 'ok':
|
||||||
cls = 'gray';
|
cls = 'green';
|
||||||
break;
|
break;
|
||||||
default: //'DISABLED', 'UNKNOWN', 'UNDEFINED'
|
case 'n/a':
|
||||||
cls = 'gray';
|
cls = 'gray';
|
||||||
break;
|
break;
|
||||||
|
default: //'DISABLED', 'UNKNOWN', 'UNDEFINED'
|
||||||
|
cls = 'gray';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var state = d.operational_state;
|
||||||
|
if (state) {
|
||||||
|
switch (state.toLowerCase()) {
|
||||||
|
case 'error':
|
||||||
|
cls = 'red';
|
||||||
|
break;
|
||||||
|
case 'suboptimal':
|
||||||
|
cls = 'yellow';
|
||||||
|
break;
|
||||||
|
case 'n/a':
|
||||||
|
cls = 'gray';
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cls;
|
return cls;
|
||||||
@@ -440,15 +501,48 @@ function hzEntitiesGraph() {
|
|||||||
|
|
||||||
|
|
||||||
if ($(this).is('.node')) {
|
if ($(this).is('.node')) {
|
||||||
d3.select(this).classed('selected', true);
|
//d3.select(this).classed('selected', true);
|
||||||
|
|
||||||
|
findHighlight(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findHighlight(rootNode) {
|
||||||
|
|
||||||
|
_.each(scope.data.nodes, function(node) {
|
||||||
|
node.high = false;
|
||||||
|
})
|
||||||
|
|
||||||
|
var depth = hightlightDepth;
|
||||||
|
|
||||||
|
findNodes(rootNode, depth, scope.data.nodes, linksMap);
|
||||||
|
|
||||||
|
_.each(scope.data.links, function(link) {
|
||||||
|
link.high = false;
|
||||||
|
})
|
||||||
|
|
||||||
|
svg_g.selectAll('.node')
|
||||||
|
.classed('selected', function(d) {
|
||||||
|
return d.high;
|
||||||
|
})
|
||||||
|
.select('circle')
|
||||||
|
.style('stroke-width', function(d) {
|
||||||
|
return d.high ? (Math.max(d.highDepth + 1, 1) * 2) : null;
|
||||||
|
})
|
||||||
|
|
||||||
|
svg_g.selectAll('.link').classed('selected', function(d) {
|
||||||
|
return d.source.high && d.target.high;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function selectNone(d) {
|
function selectNone(d) {
|
||||||
nodeClick(null);
|
nodeClick(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pinNode(d) {
|
function pinNode(d) {
|
||||||
|
d3.event.stopImmediatePropagation();
|
||||||
|
d3.event.preventDefault();
|
||||||
|
|
||||||
var node;
|
var node;
|
||||||
|
|
||||||
if ($(this).is('.node')) {
|
if ($(this).is('.node')) {
|
||||||
@@ -463,9 +557,6 @@ function hzEntitiesGraph() {
|
|||||||
updatePinnedCookie(d);
|
updatePinnedCookie(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
d3.event.stopImmediatePropagation();
|
|
||||||
d3.event.preventDefault();
|
|
||||||
|
|
||||||
//fixing some bug with unpinning
|
//fixing some bug with unpinning
|
||||||
/*setTimeout(function() {
|
/*setTimeout(function() {
|
||||||
force.resume()
|
force.resume()
|
||||||
@@ -476,7 +567,7 @@ function hzEntitiesGraph() {
|
|||||||
var pinIndex = -1;
|
var pinIndex = -1;
|
||||||
pinned.forEach(function(pin, i) {
|
pinned.forEach(function(pin, i) {
|
||||||
if (pin.id === d.id) {
|
if (pin.id === d.id) {
|
||||||
pinIndex = i
|
pinIndex = i;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -488,7 +579,7 @@ function hzEntitiesGraph() {
|
|||||||
pinned.push({id: d.id, x: d.x, y: d.y});
|
pinned.push({id: d.id, x: d.x, y: d.y});
|
||||||
}
|
}
|
||||||
|
|
||||||
horizon.cookies.put('pinned', pinned);
|
horizon.cookies.put('pinned', JSON.stringify(pinned));
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeDragend(d) {
|
function nodeDragend(d) {
|
||||||
@@ -497,6 +588,39 @@ function hzEntitiesGraph() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pinAll() {
|
||||||
|
pinned = [];
|
||||||
|
|
||||||
|
svg_g.selectAll('.node')
|
||||||
|
.classed('pinned', true)
|
||||||
|
.each(function(d) {
|
||||||
|
d.fixed = true;
|
||||||
|
pinned.push({id: d.id, x: d.x, y: d.y});
|
||||||
|
})
|
||||||
|
|
||||||
|
horizon.cookies.put('pinned', JSON.stringify(pinned));
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpinAll() {
|
||||||
|
pinned = [];
|
||||||
|
|
||||||
|
svg_g.selectAll('.node')
|
||||||
|
.classed('pinned', false)
|
||||||
|
.each(function(d) {
|
||||||
|
d.fixed = false;
|
||||||
|
})
|
||||||
|
|
||||||
|
horizon.cookies.put('pinned', JSON.stringify([]));
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
force.resume()
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
function pinAllNodes(isPin) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function setEllipsis(el, text, width) {
|
function setEllipsis(el, text, width) {
|
||||||
|
|
||||||
el.textContent = text;
|
el.textContent = text;
|
||||||
@@ -516,6 +640,30 @@ function hzEntitiesGraph() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function findNodes(rootNode, depth, allNodes, linksMap) {
|
||||||
|
if (rootNode) {
|
||||||
|
rootNode.high = true;
|
||||||
|
rootNode.highDepth = depth;
|
||||||
|
depth--;
|
||||||
|
|
||||||
|
_.each(allNodes, function(node) {
|
||||||
|
if (linksMap[node.id + ',' + rootNode.id] || linksMap[rootNode.id + ',' + node.id]) {
|
||||||
|
|
||||||
|
if (depth > -1 && !node.high) {
|
||||||
|
findNodes(node, depth, allNodes, linksMap);
|
||||||
|
} else if (depth <= -1) {
|
||||||
|
//Always find 'depth' + alarms & (sdns + alarms)
|
||||||
|
if (node.category.toLowerCase() === 'alarm') {
|
||||||
|
node.high = true;
|
||||||
|
node.highDepth = 0;
|
||||||
|
} else if (!node.high && node.type && node.type.toLowerCase() === 'sdn_controller') {
|
||||||
|
findNodes(node, depth, allNodes, linksMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*function nodeDragstart(d) {
|
/*function nodeDragstart(d) {
|
||||||
d3.select(this).classed('pinned', d.fixed = true);
|
d3.select(this).classed('pinned', d.fixed = true);
|
||||||
|
@@ -27,6 +27,11 @@ $dark_gray: darkgray;
|
|||||||
|
|
||||||
.link {
|
.link {
|
||||||
stroke-opacity: 0.8;
|
stroke-opacity: 0.8;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
stroke: $blue;
|
||||||
|
stroke-width: 2 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.node {
|
.node {
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
angular
|
||||||
|
.module('horizon.dashboard.project.vitrage')
|
||||||
|
.directive('hzEntitiesToolbox', hzEntitiesToolbox);
|
||||||
|
|
||||||
|
hzEntitiesToolbox.$inject = ['$rootScope'];
|
||||||
|
|
||||||
|
function hzEntitiesToolbox($rootScope) {
|
||||||
|
var directive = {
|
||||||
|
link: link,
|
||||||
|
templateUrl: STATIC_URL + 'dashboard/project/entities/toolbox/entities-toolbox.html',
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
item: '='
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directive;
|
||||||
|
|
||||||
|
function link(scope, element, attrs) {
|
||||||
|
scope.broadcast = function(event) {
|
||||||
|
console.log('click', event);
|
||||||
|
$rootScope.$broadcast('toolbox-' + event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
<div class="entities-toolbox">
|
||||||
|
<div class="btn-group btn-group-xs" role="group">
|
||||||
|
<a href="#" class="btn btn-default" ng-click="broadcast('pin')"><span class="fa fa-thumb-tack"></span> Pin</a>
|
||||||
|
<a href="#" class="btn btn-default" ng-click="broadcast('unpin')">Unpin</a>
|
||||||
|
</div>
|
||||||
|
<!--<div class="btn-group btn-group-xs" role="group">
|
||||||
|
<a href="#" class="btn btn-default" ng-click="broadcast('zoom-to-fit')"><span class="fa fa-expand"></span> Zoom to fit</a>
|
||||||
|
</div>
|
||||||
|
<div class="btn-group btn-group-xs" role="group">
|
||||||
|
<a href="#" class="btn btn-default" ng-click="broadcast('toggle-fullscreen')"><span class="fa fa-square-o"></span> Toggle fullscreen</a>
|
||||||
|
</div>-->
|
||||||
|
</div>
|
@@ -0,0 +1,17 @@
|
|||||||
|
.entities-toolbox {
|
||||||
|
position: absolute;
|
||||||
|
right: 18px;
|
||||||
|
margin: 12px;
|
||||||
|
padding:8px 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
margin-right: 6px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -10,6 +10,7 @@
|
|||||||
@import 'components/information/information';
|
@import 'components/information/information';
|
||||||
@import 'entities/graph/entities-graph.scss';
|
@import 'entities/graph/entities-graph.scss';
|
||||||
@import 'entities/info/entities-info.scss';
|
@import 'entities/info/entities-info.scss';
|
||||||
|
@import 'entities/toolbox/entities-toolbox.scss';
|
||||||
@import 'entities/entities.scss';
|
@import 'entities/entities.scss';
|
||||||
|
|
||||||
.red {
|
.red {
|
||||||
|
Reference in New Issue
Block a user