diff options
author | Sebastien Douheret <sebastien.douheret@iot.bzh> | 2019-07-13 10:21:53 +0200 |
---|---|---|
committer | Sebastien Douheret <sebastien.douheret@iot.bzh> | 2019-07-14 14:06:14 +0200 |
commit | 701548c5b25efba70c3818e96a4394701cfb913e (patch) | |
tree | 347881ae607acda17f2014a5e7ed1aadee055145 /webapp/src/app/pages/monitoring/monitoring-config.component.ts | |
parent | 247bb7c2db5f0d48178398599348249bf886ebbc (diff) |
Add LowCollector & rename Supervisor to Monitoringsandbox/SebD/dev_supervisor_als_2019
Change-Id: Ibd78f1200ed71b0100ebd48dddb9236377e2fb36
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
Diffstat (limited to 'webapp/src/app/pages/monitoring/monitoring-config.component.ts')
-rw-r--r-- | webapp/src/app/pages/monitoring/monitoring-config.component.ts | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/webapp/src/app/pages/monitoring/monitoring-config.component.ts b/webapp/src/app/pages/monitoring/monitoring-config.component.ts new file mode 100644 index 0000000..2a5a84b --- /dev/null +++ b/webapp/src/app/pages/monitoring/monitoring-config.component.ts @@ -0,0 +1,362 @@ +/** +* @license +* Copyright (C) 2017-2019 "IoT.bzh" +* Author Sebastien Douheret <sebastien@iot.bzh> +* +* 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, Inject } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import * as d3 from 'd3'; +import { Router } from '@angular/router'; + +import { MonitoringService, AglTopology } from '../../@core-xds/services/monitoring.service'; +import { AlertService } from '../../@core-xds/services/alert.service'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Subscription } from 'rxjs/Subscription'; + +interface WsCheckbox { + topo: AglTopology; + value: boolean; + tooltip: string; +} + +@Component({ + selector: 'xds-monitoring', + styleUrls: ['./monitoring-config.component.scss'], + templateUrl: './monitoring-config.component.html', + encapsulation: ViewEncapsulation.None, // workaround about https://github.com/angular/angular/issues/7845 +}) +export class MonitoringConfigComponent implements OnInit, AfterViewInit { + + aglTopoInit = new BehaviorSubject(false); + // FIXME: use Map instead of array and use '| keyvalue' for ngfor loop (but angular > 6.1 requested) + // daemonCheckboxes: Map<string, WsCheckbox> = new Map<string, WsCheckbox>(); + daemonCheckboxes: WsCheckbox[] = []; + starting = false; + stopping = false; + + private graph: any; + private svg: any; + private links = []; + private _aglTopoSub: Subscription; + + constructor(@Inject(DOCUMENT) private document: Document, + private router: Router, + private monitoringSvr: MonitoringService, + private alert: AlertService, + ) { + + } + + ngOnInit() { + } + + ngAfterViewInit() { + this.getAGLTopo(); + this.aglTopoInit.next(true); + } + + getAGLTopo() { + if (this._aglTopoSub !== undefined) { + this._aglTopoSub.unsubscribe(); + } + + this._aglTopoSub = this.monitoringSvr.getTopo().subscribe(topo => { + this.graphAGLBindings(topo); + this.createCheckboxes(topo); + }); + } + + onStartTrace() { + this.starting = true; + + const dmArr = []; + this.daemonCheckboxes.forEach(dm => dm.value && dmArr.push(dm.topo.pid)); + + this.monitoringSvr.startTrace({ pids: dmArr }).subscribe(res => { + // console.log('Trace Started: res', res); + + this.monitoringSvr.startLowCollector(null).subscribe((/*res*/) => { + // console.log('Low Collector Started: res', res); + this.alert.info('Monitoring successfully started'); + this.starting = false; + + }, err => { + this.starting = false; + this.alert.error(err); + }); + + }, err => { + this.starting = false; + this.alert.error(err); + }); + } + + onStopTrace() { + this.stopping = true; + this.monitoringSvr.stopTrace({}).subscribe(res => { + // console.log('Trace Stopped: res', res); + + this.monitoringSvr.stopLowCollector().subscribe((/*res*/) => { + // console.log('Low Collector Stopped: res', res); + this.alert.info('Monitoring successfully started'); + this.stopping = false; + + }, err => { + this.stopping = false; + this.alert.error(err); + }); + + }, err => { + this.stopping = false; + this.alert.error(err); + }); + } + + showGraph() { + this.router.navigate([`/pages/monitoring/graph`]); + } + + isStartBtnDisable(): boolean { + return this.starting; + } + + isStopBtnDisable(): boolean { + return this.stopping; + } + + isDaemonDisabled(name: string): boolean { + let sts = false; + // FIXME - better to use map + // with Map + // if (this.daemonCheckboxes.has(name)) { + // sts = this.daemonCheckboxes[name].value; + // } + this.daemonCheckboxes.forEach(e => { + if (e.topo.name === name) { + sts = true; + } + }); + return sts; + } + + private createCheckboxes(topo: AglTopology[]) { + + // let newDaemonChB: Map<string, WsCheckbox> = new Map<string, WsCheckbox>(); + const newDaemonChB: WsCheckbox[] = []; + let prevVal = false; + this.daemonCheckboxes.forEach(e => { + if (e.topo.name === name) { + prevVal = e.value; + } + }); + topo.forEach(elem => { + // with Map + // newDaemonChB.set(elem.name, { + newDaemonChB.push({ + topo: Object.assign({}, elem), + value: prevVal, + tooltip: 'Daemon binding ' + elem.name + ' (pid ' + elem.pid + ')', + }); + }); + + this.daemonCheckboxes = newDaemonChB; + } + + + // 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 <br/>' + 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 + ')'; + } + } +} |