3 * Copyright (C) 2017-2018 "IoT.bzh"
4 * Author Sebastien Douheret <sebastien@iot.bzh>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 import { Component, OnInit, AfterViewInit, ViewEncapsulation } from '@angular/core';
20 import { Injectable, Inject } from '@angular/core';
21 import { DOCUMENT } from '@angular/common';
22 import * as d3 from 'd3';
24 import { SupervisionService, AglTopology } from '../../@core-xds/services/supervision.service';
25 import { AlertService } from '../../@core-xds/services/alert.service';
27 interface WsCheckbox {
36 selector: 'xds-supervision',
37 styleUrls: ['./supervision-config.component.scss'],
38 templateUrl: './supervision-config.component.html',
39 encapsulation: ViewEncapsulation.None, // workaround about https://github.com/angular/angular/issues/7845
41 export class SupervisionConfigComponent implements OnInit, AfterViewInit {
43 daemonCheckboxes: WsCheckbox[] = [];
51 constructor(@Inject(DOCUMENT) private document: Document,
52 private supervisorSvr: SupervisionService,
53 private alert: AlertService,
67 this.supervisorSvr.getTopo().subscribe(topo => {
68 this.graphAGLBindings(topo);
69 this.updateCheckboxes(topo);
77 this.daemonCheckboxes.forEach(dm => dm.value && dmArr.push(dm.pid));
79 this.supervisorSvr.startTrace({ pids: dmArr }).subscribe(res => {
80 this.starting = false;
81 this.alert.info('Monitoring successfully started');
83 this.starting = false;
84 this.alert.error(err);
90 this.supervisorSvr.stopTrace({}).subscribe(res => {
91 this.stopping = false;
92 this.alert.info('Monitoring successfully stopped');
94 this.stopping = false;
95 this.alert.error(err);
99 isStartBtnDisable(): boolean {
100 return this.starting;
103 isStopBtnDisable(): boolean {
104 return this.stopping;
107 private updateCheckboxes(topo: AglTopology[]) {
108 this.daemonCheckboxes = [];
109 topo.forEach(elem => {
110 this.daemonCheckboxes.push({
115 tooltip: 'Daemon ' + elem.name + ' (pid ' + elem.pid + ')',
122 // Compute the distinct nodes from the links.
123 // Based on http://bl.ocks.org/mbostock/1153292
124 private graphAGLBindings(topo: AglTopology[]) {
126 const ws_link: { [id: string]: string[] } = {};
128 topo.forEach(elem => {
129 if (elem.name === 'null') {
130 elem.name = 'Daemon-' + String(ii++);
132 if (elem.ws_clients && elem.ws_clients instanceof Array) {
133 elem.ws_clients.forEach((ws: string) => {
135 ws_link[ws].push(elem.name);
137 ws_link[ws] = [elem.name];
141 if (elem.ws_servers && elem.ws_servers instanceof Array) {
142 elem.ws_servers.forEach((ws: string) => {
144 ws_link[ws].push(elem.name);
146 ws_link[ws] = [elem.name];
155 topo.forEach(elem => {
156 let almostOne = false;
157 if (elem.ws_clients && elem.ws_clients.length) {
158 elem.ws_clients.forEach(wsCli => {
159 ws_link[wsCli].forEach(appName => {
160 if (appName !== elem.name) {
162 this.links.push({ source: elem.name, target: appName, type: 'ws-client' });
167 if (elem.ws_servers && elem.ws_servers.length) {
168 elem.ws_servers.forEach(wsSvr => {
169 ws_link[wsSvr].forEach(appName => {
170 if (appName !== elem.name) {
172 this.links.push({ source: elem.name, target: appName, type: 'ws-server' });
178 const name = '???-' + String(ii++);
180 source: elem.isServer ? name : elem.name,
181 target: elem.isServer ? elem.name : name,
182 type: 'not-connected',
187 this.links.forEach(function (link) {
188 link.source = nodes[link.source] || (nodes[link.source] = {
191 link.target = nodes[link.target] || (nodes[link.target] = {
196 const width = this.document.getElementById('graph').clientWidth,
197 height = this.document.getElementById('graph').clientHeight;
199 // Delete previous graph
205 const force = d3.layout.force()
206 .nodes(d3.values(nodes))
208 .size([width, height])
213 // const force = d3.forceSimulation()
215 this.graph = d3.select('#graph');
216 this.svg = this.graph.append('svg')
217 .attr('width', width)
218 .attr('height', height);
220 // Define the div for the tooltip
222 const divTooltip = d3.select('#graph').append('div')
223 .attr('class', 'tooltip')
224 .style('opacity', 0);
227 // Per-type markers, as they don't inherit styles.
228 this.svg.append('defs').selectAll('marker')
229 .data(['ws-server', 'ws-client', 'not-connected'])
230 .enter().append('marker')
231 .attr('id', function (d) {
234 .attr('viewBox', '0 -5 10 10')
237 .attr('markerWidth', 12)
238 .attr('markerHeight', 12)
239 .attr('orient', 'auto')
241 .attr('d', 'M0,-5L10,0L0,5');
243 const path = this.svg.append('g').selectAll('path')
245 .enter().append('path')
246 .attr('class', function (d) {
247 return 'link ' + d.type;
249 .attr('marker-end', function (d) {
250 return 'url(#' + d.type + ')';
253 const circle = this.svg.append('g').selectAll('circle')
255 .enter().append('circle')
259 const text = this.svg.append('g').selectAll('text')
261 .enter().append('text')
269 circle.on('mouseover', d => {
270 divTooltip.transition()
272 .style('opacity', .9);
273 divTooltip.html('This is a Tooltip <br/>' + d.close)
274 .style('left', (d3.event.pageX) + 'px')
275 .style('top', (d3.event.pageY - 28) + 'px');
279 const tooltip = d3.select('body')
280 .append('div').attr('id', 'tooltip')
281 .style('position', 'absolute')
282 .style('z-index', '10')
283 .style('visibility', 'hidden')
284 .text('a simple tooltip');
287 // Use elliptical arc path segments to doubly-encode directionally.
289 path.attr('d', linkArc);
290 circle.attr('transform', transform);
291 text.attr('transform', transform);
294 function linkArc(d) {
295 const dx = d.target.x - d.source.x,
296 dy = d.target.y - d.source.y,
297 dr = Math.sqrt(dx * dx + dy * dy);
298 return 'M' + d.source.x + ',' + d.source.y + 'A' + dr + ',' + dr + ' 0 0,1 ' + d.target.x + ',' + d.target.y;
301 function transform(d) {
302 return 'translate(' + d.x + ',' + d.y + ')';