blob: ab7be1d641f6522fad624e8c0a512e32a921a92c [file] [log] [blame]
Paul Greyson30636252013-04-09 15:22:04 -07001(function () {
2
Paul Greysonc090d142013-04-09 16:59:03 -07003createTopologyView = function (cb) {
Paul Greysone15c4392013-04-09 15:05:31 -07004
5 window.addEventListener('resize', function () {
6 // this is too slow. instead detect first resize event and hide the paths that have explicit matrix applied
7 // either that or is it possible to position the paths so they get the automatic transform as well?
8// updateTopology();
9 });
10
Paul Greyson981e8c22013-04-09 17:43:59 -070011 var svg = createRootSVG();
Paul Greysone15c4392013-04-09 15:05:31 -070012
Paul Greysonc090d142013-04-09 16:59:03 -070013 topology = svg.append('svg:svg').attr('id', 'viewBox').attr('viewBox', '0 0 1000 1000').attr('preserveAspectRatio', 'none').
14 attr('id', 'viewbox').append('svg:g').attr('id', 'topology').attr('transform', 'translate(500 500)');
15
16 cb();
Paul Greysone15c4392013-04-09 15:05:31 -070017}
18
Paul Greyson30636252013-04-09 15:22:04 -070019function createRingTopologyModel(model) {
Paul Greysone15c4392013-04-09 15:05:31 -070020 var rings = [{
21 radius: 3,
22 width: widths.edge,
23 switches: model.edgeSwitches,
24 className: 'edge',
25 angles: []
26 }, {
27 radius: 2.25,
28 width: widths.aggregation,
29 switches: model.aggregationSwitches,
30 className: 'aggregation',
31 angles: []
32 }, {
33 radius: 0.75,
34 width: widths.core,
35 switches: model.coreSwitches,
36 className: 'core',
37 angles: []
38 }];
39
40
41 var aggRanges = {};
42
43 // arrange edge switches at equal increments
44 var k = 360 / rings[0].switches.length;
45 rings[0].switches.forEach(function (s, i) {
46 var angle = k * i;
47
48 rings[0].angles[i] = angle;
49
50 // record the angle for the agg switch layout
51 var dpid = s.dpid.split(':');
52 dpid[7] = '01'; // the last component of the agg switch is always '01'
53 var aggdpid = dpid.join(':');
54 var aggRange = aggRanges[aggdpid];
55 if (!aggRange) {
56 aggRange = aggRanges[aggdpid] = {};
57 aggRange.min = aggRange.max = angle;
58 } else {
59 aggRange.max = angle;
60 }
61 });
62
63 // arrange aggregation switches to "fan out" to edge switches
64 k = 360 / rings[1].switches.length;
65 rings[1].switches.forEach(function (s, i) {
66// rings[1].angles[i] = k * i;
67 var range = aggRanges[s.dpid];
68
69 rings[1].angles[i] = (range.min + range.max)/2;
70 });
71
72 // find the association between core switches and aggregation switches
73 var aggregationSwitchMap = {};
74 model.aggregationSwitches.forEach(function (s, i) {
75 aggregationSwitchMap[s.dpid] = i;
76 });
77
78 // put core switches next to linked aggregation switches
79 k = 360 / rings[2].switches.length;
80 rings[2].switches.forEach(function (s, i) {
81// rings[2].angles[i] = k * i;
82 var associatedAggregationSwitches = model.configuration.association[s.dpid];
83 // TODO: go between if there are multiple
84 var index = aggregationSwitchMap[associatedAggregationSwitches[0]];
85
86 rings[2].angles[i] = rings[1].angles[index];
87 });
88
89 // TODO: construct this form initially rather than converting. it works better because
90 // it allows binding by dpid
91 var testRings = [];
92 rings.forEach(function (ring) {
93 var testRing = [];
94 ring.switches.forEach(function (s, i) {
95 var testSwitch = {
96 dpid: s.dpid,
97 state: s.state,
98 radius: ring.radius,
99 width: ring.width,
100 className: ring.className,
101 angle: ring.angles[i],
102 controller: s.controller
103 };
104 testRing.push(testSwitch);
105 });
106
107
108 testRings.push(testRing);
109 });
110
111
112// return rings;
113 return testRings;
Paul Greyson30636252013-04-09 15:22:04 -0700114}
115
116drawTopology = function () {
117 // DRAW THE SWITCHES
Paul Greysonc090d142013-04-09 16:59:03 -0700118 var rings = topology.selectAll('.ring').data(createRingTopologyModel(model));
Paul Greyson30636252013-04-09 15:22:04 -0700119
120 function ringEnter(data, i) {
121 if (!data.length) {
122 return;
123 }
124
125 // create the nodes
126 var nodes = d3.select(this).selectAll("g")
127 .data(data, function (data) {
128 return data.dpid;
129 })
130 .enter().append("svg:g")
131 .attr("id", function (data, i) {
132 return data.dpid;
133 })
134 .attr("transform", function(data, i) {
135 return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
136 });
137
138 // add the cirles representing the switches
139 nodes.append("svg:circle")
140 .attr("transform", function(data, i) {
141 var m = document.querySelector('#viewbox').getTransformToElement().inverse();
142 if (data.scale) {
143 m = m.scale(data.scale);
144 }
145 return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
146 })
147 .attr("x", function (data) {
148 return -data.width / 2;
149 })
150 .attr("y", function (data) {
151 return -data.width / 2;
152 })
153 .attr("r", function (data) {
154 return data.width;
155 });
156 }
157
158 // append switches
159 rings.enter().append("svg:g")
160 .attr("class", "ring")
161 .each(ringEnter);
162
163
164 function ringUpdate(data, i) {
165 var nodes = d3.select(this).selectAll("g")
166 .data(data, function (data) {
167 return data.dpid;
168 });
169 nodes.select('circle')
170 .each(function (data) {
171 // if there's a pending state changed and then the state changes, clear the pending class
172 var circle = d3.select(this);
173 if (data.state === 'ACTIVE' && circle.classed('inactive') ||
174 data.state === 'INACTIVE' && circle.classed('active')) {
175 circle.classed('pending', false);
176 }
177 })
178 .attr('class', function (data) {
179 if (data.state === 'ACTIVE' && data.controller) {
180 return data.className + ' active ' + controllerColorMap[data.controller];
181 } else {
182 return data.className + ' inactive ' + 'colorInactive';
183 }
184 });
185 }
186
187 // update switches
188 rings.each(ringUpdate);
189
190
191 // Now setup the labels
192 // This is done separately because SVG draws in node order and we want the labels
193 // always on top
Paul Greysonc090d142013-04-09 16:59:03 -0700194 var labelRings = topology.selectAll('.labelRing').data(createRingTopologyModel(model));
Paul Greyson30636252013-04-09 15:22:04 -0700195
196 function labelRingEnter(data) {
197 if (!data.length) {
198 return;
199 }
200
201 // create the nodes
202 var nodes = d3.select(this).selectAll("g")
203 .data(data, function (data) {
204 return data.dpid;
205 })
206 .enter().append("svg:g")
207 .classed('nolabel', true)
208 .attr("id", function (data) {
209 return data.dpid + '-label';
210 })
211 .attr("transform", function(data, i) {
212 return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
213 })
214
215 // add the text nodes which show on mouse over
216 nodes.append("svg:text")
217 .text(function (data) {return data.dpid;})
218 .attr("x", function (data) {
219 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
220 if (data.className == 'edge') {
221 return - data.width*3 - 4;
222 } else {
223 return - data.width - 4;
224 }
225 } else {
226 if (data.className == 'edge') {
227 return data.width*3 + 4;
228 } else {
229 return data.width + 4;
230 }
231 }
232 })
233 .attr("y", function (data) {
234 var y;
235 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
236 if (data.className == 'edge') {
237 y = data.width*3/2 + 4;
238 } else {
239 y = data.width/2 + 4;
240 }
241 } else {
242 if (data.className == 'edge') {
243 y = data.width*3/2 + 4;
244 } else {
245 y = data.width/2 + 4;
246 }
247 }
248 return y - 6;
249 })
250 .attr("text-anchor", function (data) {
251 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
252 return "end";
253 } else {
254 return "start";
255 }
256 })
257 .attr("transform", function(data) {
258 var m = document.querySelector('#viewbox').getTransformToElement().inverse();
259 if (data.scale) {
260 m = m.scale(data.scale);
261 }
262 return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
263 })
264 }
265
266 labelRings.enter().append("svg:g")
267 .attr("class", "textRing")
268 .each(labelRingEnter);
269
270 // switches should not change during operation of the ui so no
271 // rings.exit()
272}
273
274})();