blob: 5769b6ff3ca61b1eb22120c5f100ae65bf10103d [file] [log] [blame]
Matteo Scandolo8cf33a32017-11-14 15:52:29 -08001/*
2 * Copyright 2017-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import './graph.component.scss';
18
19import * as d3 from 'd3';
20import * as $ from 'jquery';
21
22import {IXosGraphStore} from '../../services/graph.store';
23import {Subscription} from 'rxjs/Subscription';
24import {XosServiceGraphConfig as config} from '../../graph.config';
25import {IXosGraphHelpers} from '../../services/d3-helpers/graph-elements.helpers';
26import {IXosServiceGraphIcons} from '../../services/d3-helpers/graph-icons.service';
27import {IXosNodePositioner} from '../../services/node-positioner.service';
28import {IXosNodeRenderer} from '../../services/renderer/node.renderer';
29import {IXosSgNode} from '../../interfaces';
30import {IXosGraphConfig} from '../../services/graph.config';
31
32class XosServiceGraphCtrl {
33 static $inject = [
34 '$log',
35 '$scope',
36 'XosGraphStore',
37 'XosGraphHelpers',
38 'XosServiceGraphIcons',
39 'XosNodePositioner',
40 'XosNodeRenderer',
41 'XosGraphConfig'
42 ];
43
44 public loader: boolean = true;
45
46 private GraphSubscription: Subscription;
47 private graph: any; // this is the Graph instance
48
49 // graph element
50 private svg;
51 private linkGroup;
52 private nodeGroup;
53 private forceLayout;
54
55 constructor (
56 private $log: ng.ILogService,
57 private $scope: ng.IScope,
58 private XosGraphStore: IXosGraphStore,
59 private XosGraphHelpers: IXosGraphHelpers,
60 private XosServiceGraphIcons: IXosServiceGraphIcons,
61 private XosNodePositioner: IXosNodePositioner,
62 private XosNodeRenderer: IXosNodeRenderer,
63 private XosGraphConfig: IXosGraphConfig
64 ) {
65 this.$log.info('[XosServiceGraph] Component setup');
66
67 this.XosGraphConfig.setupKeyboardShortcuts();
68
69 this.setupSvg();
70 this.setupForceLayout();
71
72 this.GraphSubscription = this.XosGraphStore.get()
73 .subscribe(
74 graph => {
75 this.graph = graph;
76 if (this.graph.nodes().length > 0) {
77 this.loader = false;
78 this.renderGraph(this.graph);
79 }
80 },
81 error => {
82 this.$log.error('[XosServiceGraph] XosGraphStore observable error: ', error);
83 }
84 );
85
86 this.$scope.$on('xos.sg.update', () => {
87 this.$log.info(`[XosServiceGraph] Received event: xos.sg.update`);
88 this.renderGraph(this.graph);
89 });
90 }
91
92 $onDestroy() {
93 this.GraphSubscription.unsubscribe();
94 }
95
96 public closeFullscreen() {
97 this.XosGraphConfig.toggleFullscreen();
98 }
99
100 private setupSvg() {
101 this.svg = d3.select('xos-service-graph svg');
102
103 this.linkGroup = this.svg.append('g')
104 .attr({
105 class: 'link-group'
106 });
107
108 this.nodeGroup = this.svg.append('g')
109 .attr({
110 class: 'node-group'
111 });
112 }
113
114 private setupForceLayout() {
115 this.$log.debug(`[XosServiceGraph] Setup Force Layout`);
116 const tick = () => {
117 this.nodeGroup.selectAll('g.node')
118 .attr({
119 transform: d => `translate(${d.x}, ${d.y})`
120 });
121
122 this.linkGroup.selectAll('line')
123 .attr({
124 x1: l => l.source.x || 0,
125 y1: l => l.source.y || 0,
126 x2: l => l.target.x || 0,
127 y2: l => l.target.y || 0,
128 });
129 };
130
131 const svgDim = this.getSvgDimensions();
132
133 this.forceLayout =
134 d3.layout.force()
135 .size([svgDim.width, svgDim.height])
136 .on('tick', tick);
137 }
138
139 private getSvgDimensions(): {width: number, height: number} {
140 return {
141 width: $('xos-service-graph svg').width(),
142 height: $('xos-service-graph svg').height()
143 };
144 }
145
146 private renderGraph(graph: any) {
147 let nodes: IXosSgNode[] = this.XosGraphStore.nodesFromGraph(graph);
148 let links = this.XosGraphStore.linksFromGraph(graph);
149 const svgDim = this.getSvgDimensions();
150
151 this.XosNodePositioner.positionNodes(svgDim, nodes)
152 .then((nodes: IXosSgNode[]) => {
153
154 this.forceLayout
155 .nodes(nodes)
156 .links(links)
157 .size([svgDim.width, svgDim.height])
158 .linkDistance(config.force.linkDistance)
159 .charge(config.force.charge)
160 .gravity(config.force.gravity)
161 .start();
162
163 // render nodes
164 this.XosNodeRenderer.renderNodes(this.forceLayout, this.nodeGroup, nodes);
165 this.renderLinks(links);
166 });
167 }
168
169 private renderLinks(links: any[]) {
170
171 const link = this.linkGroup
172 .selectAll('line')
173 .data(links, l => l.id);
174
175 const entering = link.enter();
176
177 entering.append('line')
178 .attr({
179 id: n => n.id,
180 class: n => n.type
181 });
182
183 link.exit().remove();
184 }
185
186}
187
188export const XosServiceGraph: angular.IComponentOptions = {
189 template: require('./graph.component.html'),
190 controllerAs: 'vm',
191 controller: XosServiceGraphCtrl,
192};