blob: d33de360c0e9d50ade8dc4d37a32569047b43d1c [file] [log] [blame]
Matteo Scandoloeeb9c082016-02-09 11:19:22 -08001(function () {
2 'use strict';
3
4 angular.module('xos.serviceTopology')
Matteo Scandolo38ba3312016-02-09 16:01:49 -08005 .service('LogicTopologyHelper', function($window, $log, lodash, serviceTopologyConfig, NodeDrawer){
Matteo Scandolo11dc8c42016-02-09 14:46:14 -08006
7 var diagonal, nodes, links, i = 0, svgWidth, svgHeight, layout;
8
9 const baseData = {
10 name: 'Router',
11 type: 'router',
12 children: [
13 {
14 name: 'WAN',
15 type: 'network',
16 children: [
17 {
18 name: 'Rack',
19 type: 'rack',
20 children: [
21 {
22 name: 'LAN',
23 type: 'network',
24 children: [] //subscribers goes here
25 }
26 ]
27 }
28 ]
29 }
30 ]
31 };
32
Matteo Scandolo38ba3312016-02-09 16:01:49 -080033 /**
34 * from a nested data structure,
35 * create nodes and links for a D3 Tree Layout
36 */
Matteo Scandolo11dc8c42016-02-09 14:46:14 -080037 const computeLayout = (data) => {
38 let nodes = layout.nodes(data);
39
40 // Normalize for fixed-depth.
41 nodes.forEach(function(d) {
42 // position the child node horizontally
43 const step = ((svgWidth - (serviceTopologyConfig.widthMargin * 2)) / 7);
44 d.y = (6 - d.depth) * step;
45 });
46
47 let links = layout.links(nodes);
Matteo Scandolo38ba3312016-02-09 16:01:49 -080048
Matteo Scandolo11dc8c42016-02-09 14:46:14 -080049 return [nodes, links];
50 };
51
Matteo Scandolo38ba3312016-02-09 16:01:49 -080052 /**
53 * Draw the containing group for any node or update the existing one
54 */
Matteo Scandolo11dc8c42016-02-09 14:46:14 -080055 const drawNodes = (svg, nodes) => {
56 // Update the nodes…
57 var node = svg.selectAll('g.node')
Matteo Scandolo38ba3312016-02-09 16:01:49 -080058 .data(nodes, d => {
59 if(!angular.isString(d.d3Id)){
60 d.d3Id = `tree-${++i}`;
61 }
62 return d.d3Id;
63 });
Matteo Scandolo11dc8c42016-02-09 14:46:14 -080064
Matteo Scandolo38ba3312016-02-09 16:01:49 -080065 // Enter any new nodes
Matteo Scandolo11dc8c42016-02-09 14:46:14 -080066 var nodeEnter = node.enter().append('g')
67 .attr({
68 class: d => `node ${d.type}`,
69 transform: `translate(${svgWidth / 2}, ${svgHeight / 2})`
70 });
71
Matteo Scandolo38ba3312016-02-09 16:01:49 -080072 NodeDrawer.addNetworks(nodeEnter.filter('.network'));
73 NodeDrawer.addRack(nodeEnter.filter('.rack'));
74 NodeDrawer.addPhisical(nodeEnter.filter('.router'));
75 NodeDrawer.addPhisical(nodeEnter.filter('.subscriber'));
76 NodeDrawer.addDevice(nodeEnter.filter('.device'));
Matteo Scandolo11dc8c42016-02-09 14:46:14 -080077
78 // Transition nodes to their new position.
79 var nodeUpdate = node.transition()
80 .duration(serviceTopologyConfig.duration)
81 .attr({
82 'transform': d => `translate(${d.y},${d.x})`
83 });
84
85 // TODO handle node remove
86 };
87
Matteo Scandolo38ba3312016-02-09 16:01:49 -080088 /**
89 * Handle links in the tree layout
90 */
Matteo Scandolo11dc8c42016-02-09 14:46:14 -080091 const drawLinks = (svg, links) => {
92
93 diagonal = d3.svg.diagonal()
94 .projection(d => [d.y, d.x]);
95
96 // Update the links…
97 var link = svg.selectAll('path.link')
98 .data(links, d => {
Matteo Scandolo38ba3312016-02-09 16:01:49 -080099 return d.target.d3Id
Matteo Scandolo11dc8c42016-02-09 14:46:14 -0800100 });
101
102 // Enter any new links at the parent's previous position.
103 link.enter().insert('path', 'g')
104 .attr('class', d => `link ${d.target.type}`)
105 .attr('d', function(d) {
106 var o = {x: svgHeight / 2, y: svgWidth / 2};
107 return diagonal({source: o, target: o});
108 });
109
110 // Transition links to their new position.
111 link.transition()
112 .duration(serviceTopologyConfig.duration)
113 .attr('d', diagonal);
114 };
115
Matteo Scandolo38ba3312016-02-09 16:01:49 -0800116 /**
117 * Calculate the svg size and setup tree layout
118 */
Matteo Scandolo11dc8c42016-02-09 14:46:14 -0800119 this.drawTree = (svg) => {
120
121
122 svgWidth = svg.node().getBoundingClientRect().width;
123 svgHeight = svg.node().getBoundingClientRect().height;
124
125 const width = svgWidth - (serviceTopologyConfig.widthMargin * 2);
126 const height = svgHeight - (serviceTopologyConfig.heightMargin * 2);
127
128 layout = d3.layout.tree()
129 .size([height, width]);
130
131 // Compute the new tree layout.
132 [nodes, links] = computeLayout(baseData);
133
134 drawNodes(svg, nodes);
135 drawLinks(svg, links);
136
137 };
138
Matteo Scandolo38ba3312016-02-09 16:01:49 -0800139 /**
140 * Add Subscribers to the tree
141 */
Matteo Scandolo11dc8c42016-02-09 14:46:14 -0800142 this.addSubscribers = (svg, subscribers) => {
143
Matteo Scandolo11dc8c42016-02-09 14:46:14 -0800144 subscribers.map((subscriber) => {
145 subscriber.children = subscriber.devices;
146 });
147
148 // add subscriber to data tree
149 baseData.children[0].children[0].children[0].children = subscribers;
150
Matteo Scandolo11dc8c42016-02-09 14:46:14 -0800151 [nodes, links] = computeLayout(baseData);
152
153 drawNodes(svg, nodes);
154 drawLinks(svg, links);
155 }
Matteo Scandoloeeb9c082016-02-09 11:19:22 -0800156 });
157
158}());