diff --git a/@overflow/model/discovery/Port.ts b/@overflow/model/discovery/Port.ts index 61fe112..bd0f7b0 100644 --- a/@overflow/model/discovery/Port.ts +++ b/@overflow/model/discovery/Port.ts @@ -1,5 +1,6 @@ import { Host } from './Host'; import { MetaPortType, MetaDiscovererType } from '../meta'; +import { Service } from './Service'; export interface Port { metaPortType?: MetaPortType; @@ -10,5 +11,5 @@ export interface Port { discoveredDate?: Date; host?: Host; - // serviceList?: Service[]; + serviceList?: Service[]; } diff --git a/src/app/component/infra/display/display.component.ts b/src/app/component/infra/display/display.component.ts index 39c1f63..0659db8 100644 --- a/src/app/component/infra/display/display.component.ts +++ b/src/app/component/infra/display/display.component.ts @@ -96,4 +96,10 @@ export class DisplayComponent implements OnInit, AfterContentInit, OnDestroy { this.store.dispatch(new InfraStore.ChangeSelectedInfra(null)); } + otherHostSelected(host: Host) { + this.store.dispatch(new InfraStore.ChangeSelectedInfra({ + group: 'host', + infra: host, + })); + } } diff --git a/src/app/component/infra/display/map.component.ts b/src/app/component/infra/display/map.component.ts index e50d1e3..2ccfd06 100644 --- a/src/app/component/infra/display/map.component.ts +++ b/src/app/component/infra/display/map.component.ts @@ -29,6 +29,7 @@ import * as UILayoutStore from '../../../store/ui/layout'; import { DiscoverySession } from '../../../core/discovery/discovery-session'; import { DiscoveryMessageType } from 'src/app/core/type'; import { ConfirmationService } from 'primeng/primeng'; +import { MetaServiceTypeEnum, toMetaServiceTypeEnum } from '@overflow/model/meta/MetaServiceType'; export class DisplaySummary { totalHosts: number; @@ -91,6 +92,8 @@ export class DisplaySummary { } } +const FIRE_CLICK_DETAIL = -99999; + @Component({ selector: 'app-infra-display-map', templateUrl: './map.component.html', @@ -106,6 +109,7 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { zoneNode: Node | null = null; nodes: Node[]; links: Link[]; + nodeElements: Map; selectedNode: Node | null = null; @@ -209,6 +213,26 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { } break; case DiscoveryMessageType.DiscoveryStop: { + __this.zone.hostList = []; + __this.hosts.forEach(_host => { + if (_host.zone.network === __this.zone.network) { + __this.zone.hostList.push(_host); + } + _host.portList = []; + if (__this.ports.has(_host.address)) { + __this.ports.get(_host.address).forEach(_port => { + _host.portList.push(_port); + + _port.serviceList = []; + if (__this.services.has(_host.address) && __this.services.get(_host.address).has(_port.portNumber)) { + __this.services.get(_host.address).get(_port.portNumber).forEach(_service => { + _port.serviceList.push(_service); + }); + } + }); + } + }); + __this.simulationRestart(true); __this.zoomToFit(0.95, 500); @@ -265,7 +289,7 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { __this.onHideDetail(); return; } else { - + __this.onInfraSelected(selectedInfra); } }), ).subscribe(); @@ -325,6 +349,7 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { } this.nodes = []; this.links = []; + this.nodeElements = new Map(); this.hosts = new Map(); this.ports = new Map(); this.services = new Map(); @@ -406,7 +431,8 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { function getNodeFromElement(element: Element): Node | null { const container = d3.select(element); const nodeId = container.attr('nodeId'); - return __this.getNode(nodeId); + const { node } = __this.getNode(nodeId); + return node; } // drag @@ -474,13 +500,22 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { return false; } + nodeElements.each(function (d) { + const nodeElement = this as Element; + const node = getNodeFromElement(nodeElement); + __this.nodeElements.set(node.id, nodeElement); + }); + nodeElements.on('click', function () { d3.event.stopPropagation(); const nodeElement = this as Element; const node = getNodeFromElement(nodeElement); if (null === node || 'zone' === node.group) { - __this.onInfraClick(node); + clearSelection(); + if (FIRE_CLICK_DETAIL !== d3.event.detail) { + __this.onInfraClick(node); + } return; } @@ -535,16 +570,13 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { } }); - - __this.onInfraClick(node); + if (FIRE_CLICK_DETAIL !== d3.event.detail) { + __this.onInfraClick(node); + } }); // Highlight - const displayInfra = d3.select(this.displayInfraRef.nativeElement); - displayInfra.on('click', function () { - __this.selectedNode = null; - __this.store.dispatch(new InfraStore.ChangeSelectedInfra(null)); - + function clearSelection() { nodeElements.each(function () { const _thisElement = this as Element; d3.select(_thisElement).classed('semi-unselected', false); @@ -556,6 +588,14 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { d3.select(_thisElement).classed('semi-unselected', false); d3.select(_thisElement).classed('unselected', false); }); + + } + const displayInfra = d3.select(this.displayInfraRef.nativeElement); + displayInfra.on('click', function () { + __this.selectedNode = null; + __this.store.dispatch(new InfraStore.ChangeSelectedInfra(null)); + + clearSelection(); }); nodeElements @@ -681,33 +721,63 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { .call(this.zoomBehavior.transform, d3.zoomIdentity.translate(translate[0], translate[1]).scale(scale)); } - onInfraClick(node: Node) { - console.log(node); - - switch (node.group) { - case 'zone': - const zone: Zone = node.target; - zone.hostList = []; - - this.hosts.forEach(_host => { - if (_host.zone.network === zone.network) { - zone.hostList.push(_host); - } - }); - + onInfraSelected({ group, infra }: { group: string, infra: Host | Port | Service }) { + switch (group) { + case 'zone': { + const id = this.getZoneId(infra); + this.fireClickOnNodeElement(id); + } break; - case 'host': - const host: Host = node.target; - host.portList = []; - if (this.ports.has(host.address)) { - this.ports.get(host.address).forEach(port => { - host.portList.push(port); - }); - } + case 'host': { + const id = this.getHostId(infra); + this.fireClickOnNodeElement(id); + } + break; + case 'service': { + const id = this.getServiceId(infra); + this.fireClickOnNodeElement(id); + } + break; default: break; } + } + + + fireClickOnNodeElement(nodeId: string) { + const nodeElement = this.nodeElements.get(nodeId); + if (null === nodeElement || (null !== this.selectedNode && nodeId === this.selectedNode.id)) { + return; + } + nodeElement.dispatchEvent(new CustomEvent('click', { detail: FIRE_CLICK_DETAIL })); + } + + onInfraClick(node: Node) { + // switch (node.group) { + // case 'zone': + // const zone: Zone = node.target; + // zone.hostList = []; + + // this.hosts.forEach(_host => { + // if (_host.zone.network === zone.network) { + // zone.hostList.push(_host); + // } + // }); + + // break; + // case 'host': + // const host: Host = node.target; + // host.portList = []; + // if (this.ports.has(host.address)) { + // this.ports.get(host.address).forEach(port => { + // host.portList.push(port); + // }); + // } + // break; + // default: + // break; + // } this.store.dispatch(new InfraStore.ChangeSelectedInfra({ group: node.group, @@ -739,29 +809,60 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { } - private getNode(id: string): Node | null { + private getNode(id: string): { index: number, node: Node } { let _n: Node = null; + let _idx = -1; this.nodes.some((node): boolean => { + _idx++; if (node.id === id) { _n = node; return true; } return false; }); - return _n; + return { index: null === _n ? -1 : _idx, node: _n }; } + private getLink(source: Node, target: Node): { index: number, link: Link } | null { + let _l: Link = null; + let _idx = -1; + this.links.some((link): boolean => { + _idx++; + if (link.source.id === source.id && link.target.id === target.id) { + _l = link; + return true; + } + return false; + }); + return { index: null === _l ? -1 : _idx, link: _l }; + } refreshInfraDisplay(zone: Zone, hosts: Host[], services: Service[]) { } + getZoneId(zone: Zone): string { + return `${zone.network}`; + } + getHostId(host: Host): string { + return `${this.getZoneId(host.zone)}-${host.address}`; + } + getPortId(port: Port): string { + return `${this.getHostId(port.host)}-${port.portNumber}-${port.metaPortType.key}`; + } + getServiceId(service: Service): string { + return `${this.getPortId(service.port)}-${service.serviceType}`; + } + getUnknownServiceId(service: Service): string { + return `${this.getPortId(service.port)}-${MetaServiceTypeEnum.UNKNOWN}`; + } + setZone(zone: Zone, requireRefresh: boolean = false) { if (null === zone) { return; } this.zone = zone; - this.zoneNode = new Node(zone.network); + this.zoneNode = new Node(this.getZoneId(zone)); this.zoneNode.group = 'zone'; this.zoneNode.target = zone; this.zoneNode.fx = this.displayInfraWidth / 2; @@ -783,21 +884,21 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { } this.hosts.set(host.address, host); - const hostId = `${host.address}`; + const hostId = this.getHostId(host); - let hostNode = this.getNode(hostId); - if (null !== hostNode) { - hostNode.target = host; + let { node } = this.getNode(hostId); + if (null !== node) { + node.target = host; } else { - hostNode = new Node(hostId); - hostNode.target = host; - hostNode.group = 'host'; - hostNode.r = 40; - hostNode.x = this.zoneNode.x; - hostNode.y = this.zoneNode.y; + node = new Node(hostId); + node.target = host; + node.group = 'host'; + node.r = 40; + node.x = this.zoneNode.x; + node.y = this.zoneNode.y; - this.nodes.push(hostNode); - this.links.push(new Link(this.zoneNode, hostNode)); + this.nodes.push(node); + this.links.push(new Link(this.zoneNode, node)); this.displaySummary.increaseHost(); @@ -837,14 +938,55 @@ export class MapComponent implements OnInit, AfterContentInit, OnDestroy { return; } - const hostId = `${service.port.host.address}`; - const serviceId = `${service.port.host.address}-${service.port.portNumber}-${service.port.metaPortType.key}`; + const hostId = this.getHostId(service.port.host); + const { node: hostNode } = this.getNode(hostId); - const hostNode = this.getNode(hostId); - let serviceNode = this.getNode(serviceId); - if (null !== serviceNode) { - serviceNode.target = service; + const unknownServiceId = this.getUnknownServiceId(service); + const { index: unknownServiceNodeIdx, node: unknownServiceNode } = this.getNode(unknownServiceId); + + const serviceId = this.getServiceId(service); + let { node: serviceNode } = this.getNode(serviceId); + + let newNode = false; + + if (MetaServiceTypeEnum.UNKNOWN === MetaServiceTypeEnum[service.serviceType]) { + if (null !== unknownServiceNode) { + unknownServiceNode.target = service; + } else { + newNode = true; + } } else { + if (null !== serviceNode) { + serviceNode.target = service; + } else { + if (null !== unknownServiceNode) { + const { index: linkIdx } = this.getLink(hostNode, unknownServiceNode); + delete this.links[linkIdx]; + delete this.nodes[unknownServiceNodeIdx]; + } + newNode = true; + } + } + + let _servicesMap: Map>; + if (!this.services.has(service.port.host.address)) { + _servicesMap = new Map(); + this.services.set(service.port.host.address, _servicesMap); + } else { + _servicesMap = this.services.get(service.port.host.address); + } + + let _services: Map; + if (!_servicesMap.has(service.port.portNumber)) { + _services = new Map(); + _servicesMap.set(service.port.portNumber, _services); + } else { + _services = _servicesMap.get(service.port.portNumber); + } + + _services.set(service.serviceType, service); + + if (newNode) { serviceNode = new Node(serviceId); serviceNode.target = service; serviceNode.group = 'service';