blob: 40ec182ff5691b1edff153676ce38231c4f8e7e4 [file] [log] [blame]
Matteo Scandolod2044a42017-08-07 16:08:28 -07001
2/*
3 * Copyright 2017-present Open Networking Foundation
4
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8
9 * http://www.apache.org/licenses/LICENSE-2.0
10
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
Matteo Scandolo219b1a72016-02-09 11:19:22 -080019(function () {
20 'use strict';
21
Matteo Scandolo04564952016-02-24 11:22:48 -080022 angular.module('xos.diagnostic')
Matteo Scandolofe307b12016-05-17 14:29:01 -070023 .service('LogicTopologyHelper', function($window, $log, $rootScope, _, serviceTopologyConfig, NodeDrawer, ChartData){
Matteo Scandoloaf286372016-02-09 14:46:14 -080024
25 var diagonal, nodes, links, i = 0, svgWidth, svgHeight, layout;
26
Matteo Scandolo012dddb2016-02-22 16:53:22 -080027 const baseData = ChartData.logicTopologyData;
Matteo Scandoloaf286372016-02-09 14:46:14 -080028
Matteo Scandolo9fe01af2016-02-09 16:01:49 -080029 /**
Matteo Scandolocc0db942016-02-11 17:37:08 -080030 * Calculate the horizontal position for each element.
31 * subsrcribers, devices and routers have the same fixed width 20
32 * network have a fixed width 104
33 * rack have a fixed width 105
34 * build and array of 6 elements representing the position of each element in the svg
35 * to equally space them
36 */
37
38 this.computeElementPosition = (svgWidth) => {
39
40 let xPos = [];
41
Matteo Scandolofe307b12016-05-17 14:29:01 -070042 let totalElWidth = _.reduce(serviceTopologyConfig.elWidths, (el, val) => val + el, 0);
Matteo Scandolocc0db942016-02-11 17:37:08 -080043
Matteo Scandolobec0a6c2016-02-11 17:58:18 -080044 let remainingSpace = svgWidth - totalElWidth - (serviceTopologyConfig.widthMargin * 2);
Matteo Scandolocc0db942016-02-11 17:37:08 -080045
46 let step = remainingSpace / (serviceTopologyConfig.elWidths.length - 1);
47
Matteo Scandolofe307b12016-05-17 14:29:01 -070048 _.forEach(serviceTopologyConfig.elWidths, (el, i) => {
Matteo Scandolocc0db942016-02-11 17:37:08 -080049
50 // get half of the previous elements width
51 let previousElWidth = 0;
52 if(i !== 0){
Matteo Scandolofe307b12016-05-17 14:29:01 -070053 previousElWidth = _.reduce(serviceTopologyConfig.elWidths.slice(0, i), (el, val) => val + el, 0);
Matteo Scandolocc0db942016-02-11 17:37:08 -080054 }
55
56 let elPos =
57 serviceTopologyConfig.widthMargin // right margin
58 + (step * i) // space between elements
59 + (el / 2) // this el width
60 + previousElWidth; // previous elements width
Matteo Scandolo77d8fa02016-02-16 17:43:00 -080061
Matteo Scandolocc0db942016-02-11 17:37:08 -080062 xPos.push(svgWidth - elPos);
63 })
64
65 return xPos
66 };
67
68 /**
Matteo Scandolo9fe01af2016-02-09 16:01:49 -080069 * from a nested data structure,
70 * create nodes and links for a D3 Tree Layout
71 */
Matteo Scandoloaf286372016-02-09 14:46:14 -080072 const computeLayout = (data) => {
73 let nodes = layout.nodes(data);
74
75 // Normalize for fixed-depth.
Matteo Scandolocc0db942016-02-11 17:37:08 -080076 nodes.forEach((d) => {
Matteo Scandoloaf286372016-02-09 14:46:14 -080077 // position the child node horizontally
Matteo Scandolocc0db942016-02-11 17:37:08 -080078 d.y = this.computeElementPosition(svgWidth)[d.depth];
Matteo Scandoloaf286372016-02-09 14:46:14 -080079 });
80
81 let links = layout.links(nodes);
Matteo Scandolo9fe01af2016-02-09 16:01:49 -080082
Matteo Scandoloaf286372016-02-09 14:46:14 -080083 return [nodes, links];
84 };
85
Matteo Scandolo9fe01af2016-02-09 16:01:49 -080086 /**
87 * Draw the containing group for any node or update the existing one
88 */
Matteo Scandoloaf286372016-02-09 14:46:14 -080089 const drawNodes = (svg, nodes) => {
90 // Update the nodes…
91 var node = svg.selectAll('g.node')
Matteo Scandolo9fe01af2016-02-09 16:01:49 -080092 .data(nodes, d => {
93 if(!angular.isString(d.d3Id)){
94 d.d3Id = `tree-${++i}`;
95 }
96 return d.d3Id;
97 });
Matteo Scandoloaf286372016-02-09 14:46:14 -080098
Matteo Scandolo9fe01af2016-02-09 16:01:49 -080099 // Enter any new nodes
Matteo Scandoloaf286372016-02-09 14:46:14 -0800100 var nodeEnter = node.enter().append('g')
101 .attr({
102 class: d => `node ${d.type}`,
103 transform: `translate(${svgWidth / 2}, ${svgHeight / 2})`
104 });
105
Matteo Scandolofd468582016-02-16 16:03:43 -0800106 // create Nodes
Matteo Scandolo79de20a2016-02-16 15:06:11 -0800107 NodeDrawer.addNetworks(node.filter('.network'));
108 NodeDrawer.addRack(node.filter('.rack'));
109 NodeDrawer.addPhisical(node.filter('.router'));
110 NodeDrawer.addPhisical(node.filter('.subscriber'));
111 NodeDrawer.addDevice(node.filter('.device'));
Matteo Scandoloaf286372016-02-09 14:46:14 -0800112
Matteo Scandolo388795a2016-02-22 09:57:55 -0800113 // add event listener to subscriber
114 node.filter('.subscriber')
115 .on('click', () => {
116 $rootScope.$emit('subscriber.modal.open');
117 });
118
Matteo Scandolofd468582016-02-16 16:03:43 -0800119 //update nodes
120 // TODO if data change, only update them
121 // NodeDrawer.updateRack(node.filter('.rack'));
122
Matteo Scandoloaf286372016-02-09 14:46:14 -0800123 // Transition nodes to their new position.
124 var nodeUpdate = node.transition()
125 .duration(serviceTopologyConfig.duration)
126 .attr({
127 'transform': d => `translate(${d.y},${d.x})`
128 });
129
130 // TODO handle node remove
Matteo Scandolo45fba732016-02-22 14:53:44 -0800131 var nodeExit = node.exit().remove();
Matteo Scandoloaf286372016-02-09 14:46:14 -0800132 };
133
Matteo Scandolo9fe01af2016-02-09 16:01:49 -0800134 /**
135 * Handle links in the tree layout
136 */
Matteo Scandoloaf286372016-02-09 14:46:14 -0800137 const drawLinks = (svg, links) => {
138
139 diagonal = d3.svg.diagonal()
140 .projection(d => [d.y, d.x]);
141
142 // Update the links…
143 var link = svg.selectAll('path.link')
144 .data(links, d => {
Matteo Scandolo9fe01af2016-02-09 16:01:49 -0800145 return d.target.d3Id
Matteo Scandoloaf286372016-02-09 14:46:14 -0800146 });
147
148 // Enter any new links at the parent's previous position.
149 link.enter().insert('path', 'g')
150 .attr('class', d => `link ${d.target.type}`)
151 .attr('d', function(d) {
152 var o = {x: svgHeight / 2, y: svgWidth / 2};
153 return diagonal({source: o, target: o});
154 });
155
156 // Transition links to their new position.
157 link.transition()
158 .duration(serviceTopologyConfig.duration)
159 .attr('d', diagonal);
Matteo Scandolo45fba732016-02-22 14:53:44 -0800160
161 link.exit().remove();
Matteo Scandoloaf286372016-02-09 14:46:14 -0800162 };
163
Matteo Scandolo9fe01af2016-02-09 16:01:49 -0800164 /**
165 * Calculate the svg size and setup tree layout
166 */
Matteo Scandolo35d53c82016-02-16 14:44:51 -0800167 this.setupTree = (svg) => {
Matteo Scandoloaf286372016-02-09 14:46:14 -0800168
169
170 svgWidth = svg.node().getBoundingClientRect().width;
171 svgHeight = svg.node().getBoundingClientRect().height;
172
173 const width = svgWidth - (serviceTopologyConfig.widthMargin * 2);
174 const height = svgHeight - (serviceTopologyConfig.heightMargin * 2);
175
176 layout = d3.layout.tree()
177 .size([height, width]);
Matteo Scandolo35d53c82016-02-16 14:44:51 -0800178 };
Matteo Scandoloaf286372016-02-09 14:46:14 -0800179
Matteo Scandolo35d53c82016-02-16 14:44:51 -0800180 /**
181 * Update the tree layout
182 */
183
184 this.updateTree = (svg) => {
Matteo Scandoloaf286372016-02-09 14:46:14 -0800185 // Compute the new tree layout.
186 [nodes, links] = computeLayout(baseData);
187
Matteo Scandolo45fba732016-02-22 14:53:44 -0800188 // console.log(baseData);
Matteo Scandoloaf286372016-02-09 14:46:14 -0800189 drawNodes(svg, nodes);
190 drawLinks(svg, links);
Matteo Scandolo35d53c82016-02-16 14:44:51 -0800191 }
Matteo Scandoloaf286372016-02-09 14:46:14 -0800192
Matteo Scandolo219b1a72016-02-09 11:19:22 -0800193 });
194
195}());