X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=webapp%2Fsrc%2Fapp%2Fpages%2Fsupervision%2Fsupervision-config.component.ts;fp=webapp%2Fsrc%2Fapp%2Fpages%2Fsupervision%2Fsupervision-config.component.ts;h=e96b93647b4d3a749b84e89830fd985b1004837f;hb=ee66af78c42c4d7ff33f104415bc09d60dbdc27b;hp=0000000000000000000000000000000000000000;hpb=72c9174cecdfbe4cde9baa71c0c02d0bee753224;p=src%2Fxds%2Fxds-agent.git diff --git a/webapp/src/app/pages/supervision/supervision-config.component.ts b/webapp/src/app/pages/supervision/supervision-config.component.ts new file mode 100644 index 0000000..e96b936 --- /dev/null +++ b/webapp/src/app/pages/supervision/supervision-config.component.ts @@ -0,0 +1,305 @@ +/** +* @license +* Copyright (C) 2017-2018 "IoT.bzh" +* Author Sebastien Douheret +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { Component, OnInit, AfterViewInit, ViewEncapsulation } from '@angular/core'; +import { Injectable, Inject } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import * as d3 from 'd3'; + +import { SupervisionService, AglTopology } from '../../@core-xds/services/supervision.service'; +import { AlertService } from '../../@core-xds/services/alert.service'; + +interface WsCheckbox { + name: string; + pid: number; + value: boolean; + disabled: boolean; + tooltip: string; +} + +@Component({ + selector: 'xds-supervision', + styleUrls: ['./supervision-config.component.scss'], + templateUrl: './supervision-config.component.html', + encapsulation: ViewEncapsulation.None, // workaround about https://github.com/angular/angular/issues/7845 +}) +export class SupervisionConfigComponent implements OnInit, AfterViewInit { + + daemonCheckboxes: WsCheckbox[] = []; + starting = false; + stopping = false; + + private graph: any; + private svg: any; + private links = []; + + constructor(@Inject(DOCUMENT) private document: Document, + private supervisorSvr: SupervisionService, + private alert: AlertService, + ) { + + } + + ngOnInit() { + + } + + ngAfterViewInit() { + this.getAGLTopo(); + } + + getAGLTopo() { + this.supervisorSvr.getTopo().subscribe(topo => { + this.graphAGLBindings(topo); + this.updateCheckboxes(topo); + }); + } + + onStartTrace() { + this.starting = true; + + const dmArr = []; + this.daemonCheckboxes.forEach(dm => dm.value && dmArr.push(dm.pid)); + + this.supervisorSvr.startTrace({ pids: dmArr }).subscribe(res => { + this.starting = false; + this.alert.info('Monitoring successfully started'); + }, err => { + this.starting = false; + this.alert.error(err); + }); + } + + onStopTrace() { + this.stopping = true; + this.supervisorSvr.stopTrace({}).subscribe(res => { + this.stopping = false; + this.alert.info('Monitoring successfully stopped'); + }, err => { + this.stopping = false; + this.alert.error(err); + }); + } + + isStartBtnDisable(): boolean { + return this.starting; + } + + isStopBtnDisable(): boolean { + return this.stopping; + } + + private updateCheckboxes(topo: AglTopology[]) { + this.daemonCheckboxes = []; + topo.forEach(elem => { + this.daemonCheckboxes.push({ + name: elem.name, + pid: elem.pid, + value: false, + disabled: false, + tooltip: 'Daemon ' + elem.name + ' (pid ' + elem.pid + ')', + }); + }); + + } + + + // Compute the distinct nodes from the links. + // Based on http://bl.ocks.org/mbostock/1153292 + private graphAGLBindings(topo: AglTopology[]) { + + const ws_link: { [id: string]: string[] } = {}; + let ii = 1; + topo.forEach(elem => { + if (elem.name === 'null') { + elem.name = 'Daemon-' + String(ii++); + } + if (elem.ws_clients && elem.ws_clients instanceof Array) { + elem.ws_clients.forEach((ws: string) => { + if (ws_link[ws]) { + ws_link[ws].push(elem.name); + } else { + ws_link[ws] = [elem.name]; + } + }); + } + if (elem.ws_servers && elem.ws_servers instanceof Array) { + elem.ws_servers.forEach((ws: string) => { + if (ws_link[ws]) { + ws_link[ws].push(elem.name); + } else { + ws_link[ws] = [elem.name]; + } + }); + } + }); + + const nodes = {}; + this.links = []; + ii = 1; + topo.forEach(elem => { + let almostOne = false; + if (elem.ws_clients && elem.ws_clients.length) { + elem.ws_clients.forEach(wsCli => { + ws_link[wsCli].forEach(appName => { + if (appName !== elem.name) { + almostOne = true; + this.links.push({ source: elem.name, target: appName, type: 'ws-client' }); + } + }); + }); + } + if (elem.ws_servers && elem.ws_servers.length) { + elem.ws_servers.forEach(wsSvr => { + ws_link[wsSvr].forEach(appName => { + if (appName !== elem.name) { + almostOne = true; + this.links.push({ source: elem.name, target: appName, type: 'ws-server' }); + } + }); + }); + } + if (!almostOne) { + const name = '???-' + String(ii++); + this.links.push({ + source: elem.isServer ? name : elem.name, + target: elem.isServer ? elem.name : name, + type: 'not-connected', + }); + } + }); + + this.links.forEach(function (link) { + link.source = nodes[link.source] || (nodes[link.source] = { + name: link.source, + }); + link.target = nodes[link.target] || (nodes[link.target] = { + name: link.target, + }); + }); + + const width = this.document.getElementById('graph').clientWidth, + height = this.document.getElementById('graph').clientHeight; + + // Delete previous graph + if (this.svg) { + this.svg.remove(); + } + + // Create new graph + const force = d3.layout.force() + .nodes(d3.values(nodes)) + .links(this.links) + .size([width, height]) + .linkDistance(120) + .charge(-600) + .on('tick', tick) + .start(); + // const force = d3.forceSimulation() + + this.graph = d3.select('#graph'); + this.svg = this.graph.append('svg') + .attr('width', width) + .attr('height', height); + + // Define the div for the tooltip + /* + const divTooltip = d3.select('#graph').append('div') + .attr('class', 'tooltip') + .style('opacity', 0); + */ + + // Per-type markers, as they don't inherit styles. + this.svg.append('defs').selectAll('marker') + .data(['ws-server', 'ws-client', 'not-connected']) + .enter().append('marker') + .attr('id', function (d) { + return d; + }) + .attr('viewBox', '0 -5 10 10') + .attr('refX', 15) + .attr('refY', -1.5) + .attr('markerWidth', 12) + .attr('markerHeight', 12) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M0,-5L10,0L0,5'); + + const path = this.svg.append('g').selectAll('path') + .data(force.links()) + .enter().append('path') + .attr('class', function (d) { + return 'link ' + d.type; + }) + .attr('marker-end', function (d) { + return 'url(#' + d.type + ')'; + }); + + const circle = this.svg.append('g').selectAll('circle') + .data(force.nodes()) + .enter().append('circle') + .attr('r', 12) + .call(force.drag); + + const text = this.svg.append('g').selectAll('text') + .data(force.nodes()) + .enter().append('text') + .attr('x', 20) + .attr('y', '.31em') + .text(function (d) { + return d.name; + }); + + /* TODO - SEB + circle.on('mouseover', d => { + divTooltip.transition() + .duration(200) + .style('opacity', .9); + divTooltip.html('This is a Tooltip
' + d.close) + .style('left', (d3.event.pageX) + 'px') + .style('top', (d3.event.pageY - 28) + 'px'); + }); + + // Tooltip Object + const tooltip = d3.select('body') + .append('div').attr('id', 'tooltip') + .style('position', 'absolute') + .style('z-index', '10') + .style('visibility', 'hidden') + .text('a simple tooltip'); + */ + + // Use elliptical arc path segments to doubly-encode directionally. + function tick() { + path.attr('d', linkArc); + circle.attr('transform', transform); + text.attr('transform', transform); + } + + function linkArc(d) { + const dx = d.target.x - d.source.x, + dy = d.target.y - d.source.y, + dr = Math.sqrt(dx * dx + dy * dy); + return 'M' + d.source.x + ',' + d.source.y + 'A' + dr + ',' + dr + ' 0 0,1 ' + d.target.x + ',' + d.target.y; + } + + function transform(d) { + return 'translate(' + d.x + ',' + d.y + ')'; + } + } +}