import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { Network, DataSet, Options } from 'vis';
import { BehaviorSubject } from 'rxjs';
import { RouterUIModel } from '../../../router/routers/router.model';
import { Circuit } from '../../../circuit/circuits/circuit.model';
import { RouterService } from '../../../router/routers/router.service';
import { CircuitService } from '../../../circuit/circuits/circuit.service';
import { MatDialog } from '@angular/material';
import { SaveGraphComponent } from '../save-graph/save-graph.component';
import { CircuitGraphModel, NetworkGraph, RouterGraphModel, NetworkModel, PositionJson } from '../network-graph.model';
import { GetSavedGraphComponent } from '../get-saved-graphs/get-saved-graphs.component';
import { NetworkGraphService } from '../network-graph.service';
import { TranslateService } from '@ngx-translate/core';
import { LayoutUtilsService } from '../../../../../../core/services/layout-utils.service';
import { AggregationDefinition } from '../../../circuit/aggregation-definition/aggregation-definition.model';
import { AggregationDefinitionService } from '../../../circuit/aggregation-definition/aggregation-definition.service';
import { AlertifyService } from '../../../../../../core/services/alertify.service';
@Component({
selector: 'network-graph-list',
templateUrl: './network-graph-list.component.html',
styleUrls: ['./network-graph-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NetworkGraphListComponent implements OnInit {
network: Network = null;
nodes: any = new DataSet();
edges: any = new DataSet();
fromRouterId: number = 0;
fromRouterNode: RouterUIModel = new RouterUIModel();
toRouterId: number = 0;
toRouterNode: RouterUIModel = new RouterUIModel();
routerNode: RouterUIModel = new RouterUIModel();
routerSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
changeSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
circuitToAggSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
aggToCircuitSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
circuitEdgeId: number = 0;
circuitEdge: Circuit = new Circuit();
aggDefId: number = 0;
aggDef: AggregationDefinition;
networkGraphId: number = 0;
container: any;
constructor(
private cdr: ChangeDetectorRef,
private routerService: RouterService,
private circuitService: CircuitService,
private dialog: MatDialog,
private translate: TranslateService,
private networkGraphService: NetworkGraphService,
private layoutUtilsService: LayoutUtilsService,
private aggDefService: AggregationDefinitionService,
private alertifyService: AlertifyService
) {
}
ngOnInit(): void {
this.routerSubject.subscribe((routerSubjectId: number) => {
this.routerService.getById(routerSubjectId).subscribe(res => {
this.routerNode = res;
});
});
this.draw();
this.cdr.detectChanges();
}
draw(positions?: PositionJson[]) {
try {
this.container = document.getElementById("NetworkGraph")!;
var options = {
physics: {
enabled: false
},
interaction: {
hover: true
}
};
this.network = new Network(this.container, {nodes: this.nodes,edges: this.edges}, options);
if(positions != null){
for(var position of positions){
this.network.moveNode(position.nodeId,position.nodeX,position.nodeY);
}
}
this.network.setOptions({
});
// this.network.on("selectNode", function (params) {
// console.log("selectNode Event:", params);
// });
// this.network.on("selectEdge", function (params) {
// console.log("selectEdge Event:", params);
// });
// this.network.on("deselectNode", function (params) {
// console.log("deselectNode Event:", params);
// });
// this.network.on("deselectEdge", function (params) {
// console.log("deselectEdge Event:", params);
// });
this.network.on("hoverNode", (res) => {
this.showTooltip(res);
});
this.network.on("blurNode", (res) => {
this.removeTooltip(res);
});
}
catch(err){
console.log(err);
}
this.cdr.detectChanges();
}
showTooltip(res){
this.container.appendChild(this.makeUlTooltip(["abc","def"]));
this.container.innerHtml += this.makeUlTooltip(["abc","def"]);
}
removeTooltip(res){
document.getElementById("nodeTooltip").remove();
}
makeUlTooltip(array: string[]) {
var list = document.createElement('ul');
list.style.backgroundColor = "green";
list.style.display = "block";
list.style.position = "absolute";
list.id = "nodeTooltip";
list.style.zIndex = "100";
for (var i = 0; i < array.length; i++) {
var item = document.createElement('li');
item.appendChild(document.createTextNode(array[i]));
list.appendChild(item);
}
return list;
}
addNode() {
try {
if(this.routerNode === null){
this.alertifyService.error("Please Select Router.");
return;
}
else if(!Object.values(this.nodes._data as Object).some(o => o.id === this.routerNode.id)) {
this.nodes.add({
id: this.routerNode.id,
label: this.routerNode.name,
shape: "circle",
color: "#3F51B5",
font: { size: 16, color: "white", face: "arial" },
margin: { top: 15, right: 15, bottom: 15, left: 15 },
});
this.nextRouterSubjectFunction();
this.nextChangeSubjectFunction();
}
else {
this.alertifyService.error("Router is in the Graphic.");
return;
}
} catch (err) {
console.log(err);
}
this.cdr.detectChanges();
}
removeNode() {
try {
if(this.routerNode == null){
this.alertifyService.error("Router Not Selected.");
return;
}
if(!Object.values(this.nodes._data as Object).some(o => o.id === this.routerNode.id)){
this.alertifyService.error("Router Not Found.");
return;
}
else {
this.nodes.remove({ id: this.routerNode.id });
for(var releatedFromNodeEdge of Object.values(this.edges._data as Object).filter(o => o.from === this.routerNode.id)){
this.edges.remove({ id: releatedFromNodeEdge.id });
}
for(var releatedToNodeEdge of Object.values(this.edges._data as Object).filter(o => o.to === this.routerNode.id)){
this.edges.remove({ id: releatedToNodeEdge.id });
}
this.draw(this.preparePosition());
this.nextRouterSubjectFunction();
this.nextChangeSubjectFunction();
}
} catch (err) {
console.log(err);
}
this.cdr.detectChanges();
}
addEdge() {
try {
if(Object.keys(this.circuitEdge).length === 0){
this.alertifyService.error("Please Select Circuit.");
return;
}
else if(!Object.values(this.edges._data as Object).some(o => o.id === this.circuitEdge.id)){
this.edges.add({
id: this.circuitEdge.id,
from: this.fromRouterNode.id,
to: this.toRouterNode.id,
length: 400,
arrows: { from: { enabled: false, scaleFactor : 1}, to: { enabled: true, scaleFactor: 0.5 }},
label: this.circuitEdge.name+"\nInput Bps: "+this.circuitEdge.dataUtilization.inputBps+" bps"+"\nOutput Bps: "+this.circuitEdge.dataUtilization.outputBps+" bps",
color: { color: "red" },
type: this.circuitEdge.type
});
}
else {
this.alertifyService.error("Circuit is in the Graphic.");
return;
}
} catch (err) {
console.log(err);
}
this.cdr.detectChanges();
}
removeEdge() {
try {
if(Object.keys(this.circuitEdge).length === 0){
this.alertifyService.error("Please Select Circuit.");
return;
}
if(!Object.values(this.edges._data as Object).some(o => o.id === this.circuitEdge.id)){
this.alertifyService.error("Circuit Not Found.");
return;
}
else {
this.edges.remove({ id: this.circuitEdge.id });
this.draw(this.preparePosition());
}
} catch (err) {
console.log(err);
}
this.cdr.detectChanges();
}
saveGraph(){
if(this.networkGraphId > 0){
this.alertifyService.error("Network Graph Already Saved.");
return;
}
var circuitGraphModels: CircuitGraphModel[] = [];
for(var circuit of Object.values(this.edges._data as Object)){
var circuitGraphModel: CircuitGraphModel = new CircuitGraphModel();
circuitGraphModel.id = circuit.id;
circuitGraphModel.label = circuit.label;
circuitGraphModel.to = circuit.to;
circuitGraphModel.from = circuit.from;
circuitGraphModel.arrows = JSON.stringify(circuit.arrows);
circuitGraphModel.type = circuit.type;
circuitGraphModels.push(circuitGraphModel);
}
var routerGraphModels: RouterGraphModel[] = [];
for(var router of Object.values(this.nodes._data as Object)){
var routerGraphModel: RouterGraphModel = new RouterGraphModel();
routerGraphModel.id = router.id;
routerGraphModel.label = router.label;
routerGraphModels.push(routerGraphModel);
}
var networkModel: NetworkModel = new NetworkModel();
networkModel.clear();
networkModel.circuitGraphModels = circuitGraphModels;
networkModel.routerGraphModels = routerGraphModels;
networkModel.positionJsons = this.preparePosition();
var networkGraph: NetworkGraph = new NetworkGraph();
networkGraph.clear();
networkGraph.networkModel = networkModel;
const dialogRef = this.dialog.open(SaveGraphComponent, { width: "500px", data: { networkGraph } });
dialogRef.afterClosed().subscribe(res => {
if(res === 0){
return;
}
else if(res.Data === 0){
return;
}
else if(res.data > 0){
this.networkGraphId = res.data;
}
else {
return;
}
});
}
updateGraph(){
if(this.networkGraphId === 0){
this.alertifyService.error("Please Save Graph");
return;
}
var circuitGraphModels: CircuitGraphModel[] = [];
for(var circuit of Object.values(this.edges._data as Object)){
var circuitGraphModel: CircuitGraphModel = new CircuitGraphModel();
circuitGraphModel.id = circuit.id;
circuitGraphModel.label = circuit.label;
circuitGraphModel.to = circuit.to;
circuitGraphModel.from = circuit.from;
circuitGraphModel.arrows = JSON.stringify(circuit.arrows);
circuitGraphModel.type = circuit.type;
circuitGraphModels.push(circuitGraphModel);
}
var routerGraphModels: RouterGraphModel[] = [];
for(var router of Object.values(this.nodes._data as Object)){
var routerGraphModel: RouterGraphModel = new RouterGraphModel();
routerGraphModel.id = router.id;
routerGraphModel.label = router.label;
routerGraphModels.push(routerGraphModel);
}
var networkModel: NetworkModel = new NetworkModel();
networkModel.clear();
networkModel.circuitGraphModels = circuitGraphModels;
networkModel.routerGraphModels = routerGraphModels;
networkModel.positionJsons = this.preparePosition();
var networkGraph: NetworkGraph = new NetworkGraph();
networkGraph.clear();
networkGraph.id = this.networkGraphId;
networkGraph.networkModel = networkModel;
this.networkGraphService.getById(networkGraph.id).subscribe(ng => {
networkGraph.name = ng.name;
const dialogRef = this.dialog.open(SaveGraphComponent, { width: "500px", data: { networkGraph } });
dialogRef.afterClosed().subscribe(res => {
if(res === 0){
return;
}
else if(res.Data === 0){
return;
}
else if(res.data > 0){
this.networkGraphId = res.data;
}
else {
return;
}
});
});
}
getSavedGraphs(){
const dialogRef = this.dialog.open(GetSavedGraphComponent, { width: "500px", data: { } });
dialogRef.afterClosed().subscribe(networkGraph => {
if(networkGraph === 0){
return;
}
else {
this.networkGraphId = networkGraph.id;
this.nextChangeSubjectFunction();
this.nodes = new DataSet();
for(var routerNode of networkGraph.networkModel.routerGraphModels){
this.nodes.add({
id: routerNode.id,
label: routerNode.label,
shape: "circle",
color: "#3F51B5",
font: { size: 16, color: "white", face: "arial" },
margin: { top: 15, right: 15, bottom: 15, left: 15 }
});
}
this.edges = new DataSet();
for(var circuitEdge of networkGraph.networkModel.circuitGraphModels){
this.edges.add({
id: circuitEdge.id,
from: circuitEdge.from,
to: circuitEdge.to,
length: 400,
arrows: JSON.parse(circuitEdge.arrows),
label: circuitEdge.label,
color: { color: "red" },
type: circuitEdge.type
});
}
}
this.draw(networkGraph.networkModel.positionJsons);
this.nextChangeSubjectFunction();
});
}
preparePosition(): PositionJson[] {
var positionJsons: PositionJson[] = [];
for(var position of Object.keys(this.network.getPositions() as Object)){
var positionJson: PositionJson = new PositionJson();
positionJson.nodeId = Number(position);
positionJson.nodeX = this.network.getPositions()[position].x;
positionJson.nodeY = this.network.getPositions()[position].y;
positionJsons.push(positionJson);
}
return positionJsons;
}
deleteGraph(){
const _title: string = this.translate.instant('COMMON.DELETE.TITLE');
const _description: string = this.translate.instant('COMMON.DELETE.DESC');
const _waitDescription: string = this.translate.instant('COMMON.DELETE.WAIT_DESC');
const dialogRef = this.layoutUtilsService.deleteElement(_title, _description, _waitDescription);
dialogRef.componentInstance.yesClick.subscribe((res) => {
this.networkGraphService.delete(this.networkGraphId).subscribe(() => {
if(this.networkGraphId > 0){
dialogRef.close();
this.networkGraphId = 0;
this.nodes = new DataSet();
this.edges = new DataSet();
this.draw();
this.nextChangeSubjectFunction();
}
else {
dialogRef.close();
this.alertifyService.error("Network Graph Not Saved.");
}
});
});
}
refreshGraph(){
this.networkGraphId = 0;
this.nodes = new DataSet();
this.edges = new DataSet();
this.draw();
this.nextChangeSubjectFunction();
}
nextRouterSubjectFunction(){
this.routerSubject.next(0);
}
nextChangeSubjectFunction(){
this.changeSubject.next(0);
}
changeCircuitEdge(){
this.nextAggToCircuitSubjectFunction();
this.circuitService.getCircuitById(this.circuitEdgeId).subscribe(res => {
this.circuitEdge = res;
this.circuitEdge.type = "Circuit";
});
}
changeAggregationDefinition(){
this.nextCircuitToAggSubjectFunction();
this.aggDefService.getById(this.aggDefId).subscribe(res => {
this.circuitEdge = res;
this.circuitEdge.type = "Aggregation";
});
}
changeFromRouter(){
this.routerService.getById(this.fromRouterId).subscribe(res => {
this.fromRouterNode = res;
});
}
changeToRouter(){
this.routerService.getById(this.toRouterId).subscribe(res => {
this.toRouterNode = res;
});
}
nextCircuitToAggSubjectFunction(){
this.circuitToAggSubject.next(0);
}
nextAggToCircuitSubjectFunction(){
this.aggToCircuitSubject.next(0);
}
}
html,
body {
font: 11pt arial;
}
h1 {
font-size: 150%;
margin: 5px 0;
}
h2 {
font-size: 100%;
margin: 5px 0;
}
table.view {
width: 100%;
}
table td {
vertical-align: top;
}
table table {
background-color: #f5f5f5;
border: 1px solid #e5e5e5;
}
table table td {
vertical-align: middle;
}
input[type="text"],
pre {
border: 1px solid lightgray;
}
pre {
margin: 0;
padding: 5px;
font-size: 10pt;
}
#NetworkGraph {
width: 100%;
height: 700px;
}
<ng-container mPortletHeadTitle>
<div class="m-portlet__head-title">
<h4 class="m-portlet__head-text">
<span translate="Network Graph"></span>
</h4>
</div>
</ng-container>
<br />
<div class="row">
<div class="col-xl-2">
<div class="mat-table__wrapper">
<tbody>
<tr>
<td>
<table>
<thead>
<tr>
<th>
<h6 class="m-portlet__head-text">
<span translate="Routers"></span>
</h6>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<router-subject-autocomplete
[modelSubject]="routerSubject"
[placeholder]="'Router'"
></router-subject-autocomplete>
</td>
</tr>
<tr>
<td>
<button
id="node-add"
(click)="addNode()"
mat-raised-button
matTooltip="{{ 'Add' | translate }}"
[style.background-color]="'#E60000'"
[style.color]="'white'"
type="button"
>
<span translate="Add"></span>
</button>
<button
id="node-remove"
(click)="removeNode()"
mat-raised-button
matTooltip="{{
'Delete' | translate
}}"
[style.background-color]="'#E60000'"
[style.color]="'white'"
type="button"
>
<span translate="Delete"></span>
</button>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<br />
<hr />
<br />
<tr>
<td>
<table>
<thead>
<tr>
<th>
<h6 class="m-portlet__head-text">
<span translate="Circuits"></span>
</h6>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<circuit-autocomplete
[(model)]="circuitEdgeId"
[circuitToAggSubject]="circuitToAggSubject"
(modelChange)="changeCircuitEdge()"
></circuit-autocomplete>
</td>
</tr>
<tr>
<td>
<agg-def-single-autocomplete
[(model)]="aggDefId"
[aggToCircuitSubject]="aggToCircuitSubject"
(modelChange)="
changeAggregationDefinition()
"
></agg-def-single-autocomplete>
</td>
</tr>
<tr>
<td>
<router-node-autocomplete
[(model)]="fromRouterId"
[changeSubject]="changeSubject"
(modelChange)="changeFromRouter()"
[(nodes)]="nodes"
[placeholder]="'From Router'"
></router-node-autocomplete>
</td>
</tr>
<tr>
<td>
<router-node-autocomplete
[(model)]="toRouterId"
[changeSubject]="changeSubject"
(modelChange)="changeToRouter()"
[(nodes)]="nodes"
[placeholder]="'To Router'"
></router-node-autocomplete>
</td>
</tr>
<tr>
<td>
<button
id="edge-add"
(click)="addEdge()"
mat-raised-button
matTooltip="{{ 'Add' | translate }}"
[style.background-color]="'#E60000'"
[style.color]="'white'"
type="button"
>
<span translate="Add"></span>
</button>
<button
id="edge-remove"
(click)="removeEdge()"
mat-raised-button
matTooltip="{{
'Delete' | translate
}}"
[style.background-color]="'#E60000'"
[style.color]="'white'"
type="button"
>
<span translate="Delete"></span>
</button>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</div>
</div>
<div class="col-xl-10">
<h1>Network</h1>
<table class="view" style="background-color: white">
<colgroup>
<col width="50%" />
</colgroup>
<tbody>
<tr>
<td>
<div style="padding: 15px">
<button
(click)="saveGraph()"
mat-icon-button
matTooltip="{{ 'Save Graph' | translate }}"
color="primary"
type="button"
>
<mat-icon>create</mat-icon></button
>
<button
(click)="updateGraph()"
mat-icon-button
matTooltip="{{ 'Update Graph' | translate }}"
color="primary"
type="button"
>
<mat-icon>update</mat-icon></button
>
<button
mat-icon-button
color="primary"
matTooltip="{{ 'saved graphs' | translate }}"
(click)="getSavedGraphs()"
>
<mat-icon>file_copy</mat-icon></button
>
<button
mat-icon-button
color="warn"
matTooltip="{{
'COMMON.BUTTONS.DELETE' | translate
}}"
type="button"
(click)="deleteGraph()"
>
<mat-icon>delete</mat-icon>
</button>
<button
mat-icon-button
color="primary"
matTooltip="{{
'refresh' | translate
}}"
type="button"
(click)="refreshGraph()"
>
<mat-icon>sync</mat-icon>
</button>
</div>
<div id="NetworkGraph"></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>