| /* |
| * Copyright 2019-present Open Networking Foundation |
| * |
| * 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 { |
| AfterViewInit, |
| Component, HostListener, Inject, Input, |
| OnDestroy, |
| OnInit, SimpleChange, |
| ViewChild |
| } from '@angular/core'; |
| import * as d3 from 'd3'; |
| import { |
| FnService, |
| IconService, |
| KeysService, |
| KeysToken, |
| LionService, |
| LogService, |
| PrefsService, |
| SvgUtilService, |
| TopoZoomPrefs, |
| WebSocketService, |
| ZoomUtils |
| } from 'org_onosproject_onos/web/gui2-fw-lib/public_api'; |
| import {InstanceComponent} from '../panel/instance/instance.component'; |
| import {DetailsComponent} from '../panel/details/details.component'; |
| import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component'; |
| import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component'; |
| import {Intent, TopologyService} from '../topology.service'; |
| import { |
| GridDisplayToggle, |
| HostLabelToggle, |
| LabelToggle, |
| UiElement |
| } from '../layer/forcesvg/models'; |
| import { |
| ALL_TRAFFIC, |
| BKGRND_SELECT, |
| BKGRND_TOGGLE, |
| CANCEL_TRAFFIC, |
| CYCLEGRIDDISPLAY_BTN, |
| CYCLEHOSTLABEL_BTN, |
| CYCLELABELS_BTN, |
| DETAILS_TOGGLE, |
| EQMASTER_BTN, |
| HOSTS_TOGGLE, |
| INSTANCE_TOGGLE, |
| LAYOUT_ACCESS_BTN, |
| LAYOUT_DEFAULT_BTN, |
| OFFLINE_TOGGLE, |
| PORTS_TOGGLE, |
| QUICKHELP_BTN, |
| RESETZOOM_BTN, |
| SUMMARY_TOGGLE, |
| ALARMS_TOGGLE |
| } from '../panel/toolbar/toolbar.component'; |
| import {TrafficService, TrafficType} from '../traffic.service'; |
| import {ZoomableDirective} from 'org_onosproject_onos/web/gui2-fw-lib/public_api'; |
| import {MapObject} from '../layer/maputils'; |
| import {LayoutService, LayoutType} from '../layout.service'; |
| import {SelectedEvent} from '../layer/forcesvg/visuals/nodevisual'; |
| import {ActivatedRoute} from '@angular/router'; |
| |
| const TOPO2_PREFS = 'topo2_prefs'; |
| const TOPO_MAPID_PREFS = 'topo_mapid'; |
| |
| const PREF_BG = 'bg'; |
| const PREF_DETAIL = 'detail'; |
| const PREF_DLBLS = 'dlbls'; |
| const PREF_HLBLS = 'hlbls'; |
| const PREF_GRID = 'grid'; |
| const PREF_HOSTS = 'hosts'; |
| const PREF_INSTS = 'insts'; |
| const PREF_OFFDEV = 'offdev'; |
| const PREF_PORTHL = 'porthl'; |
| const PREF_SUMMARY = 'summary'; |
| const PREF_TOOLBAR = 'toolbar'; |
| const PREF_PINNED = 'pinned'; |
| const PREF_TRAFFIC = 'traffic'; |
| const PREF_ALARMS = 'alarms'; |
| |
| const BACKGROUND_ELEMENTS = [ |
| 'svg topo2', |
| 'path bgmap' |
| ]; |
| |
| /** |
| * Model of the topo2_prefs object - this is a subset of the overall Prefs returned |
| * by the server |
| */ |
| export interface Topo2Prefs { |
| bg: number; |
| detail: number; |
| dlbls: number; |
| hlbls: number; |
| hosts: number; |
| insts: number; |
| offdev: number; |
| porthl: number; |
| spr: number; |
| ovid: string; |
| summary: number; |
| toolbar: number; |
| grid: number; |
| pinned: number; |
| traffic: number; |
| alarms: number; |
| } |
| |
| /** |
| * ONOS GUI Topology View |
| * |
| * This Topology View component is the top level component in a hierarchy that |
| * comprises the whole Topology View |
| * |
| * There are three main parts (panels, graphical and breadcrumbs) |
| * The panel hierarchy |
| * |-- Instances Panel (shows ONOS instances) |
| * |-- Summary Panel (summary of ONOS) |
| * |-- Toolbar Panel (the toolbar) |
| * |-- Details Panel (when a node is selected in the Force graphical view (see below)) |
| * |
| * The graphical hierarchy contains |
| * Topology (this) |
| * |-- No Devices Connected (only of there are no nodes to show) |
| * |-- Zoom Layer (everything beneath this can be zoomed and panned) |
| * |-- Background (container for any backgrounds - can be toggled on and off) |
| * |-- Map |
| * |-- Forces (all of the nodes and links laid out by a d3.force simulation) |
| * |
| * The breadcrumbs |
| * |-- Breadcrumb (in region view a way of navigating back up through regions) |
| */ |
| @Component({ |
| selector: 'onos-topology', |
| templateUrl: './topology.component.html', |
| styleUrls: ['./topology.component.css'] |
| }) |
| export class TopologyComponent implements OnInit, OnDestroy, AfterViewInit { |
| @Input() bannerHeight: number = 48; |
| // These are references to the components inserted in the template |
| @ViewChild(InstanceComponent, {static: true}) instance: InstanceComponent; |
| @ViewChild(DetailsComponent, {static: true}) details: DetailsComponent; |
| @ViewChild(BackgroundSvgComponent, {static: true}) background: BackgroundSvgComponent; |
| @ViewChild(ForceSvgComponent, {static: true}) force: ForceSvgComponent; |
| @ViewChild(ZoomableDirective, {static: true}) zoomDirective: ZoomableDirective; |
| |
| flashMsg: string = ''; |
| // These are used as defaults if nothing is set on the server |
| prefsState: Topo2Prefs = <Topo2Prefs>{ |
| bg: 0, |
| detail: 1, |
| dlbls: 0, |
| hlbls: 2, |
| hosts: 0, |
| insts: 1, |
| offdev: 1, |
| ovid: 'traffic', // default to traffic overlay |
| porthl: 1, |
| spr: 0, |
| summary: 1, |
| toolbar: 0, |
| grid: 0, |
| pinned: 0, |
| traffic: 2, // default to PORTSTATSPKTSEC, as it will iterate over to 0 on init |
| alarms: 1, |
| }; |
| |
| mapIdState: MapObject = <MapObject>{ |
| id: undefined, |
| scale: 1.0 |
| }; |
| mapSelShown: boolean = false; |
| lionFn; // Function |
| |
| gridShown: boolean = true; |
| geoGridShown: boolean = true; |
| |
| constructor( |
| protected log: LogService, |
| protected fs: FnService, |
| protected ks: KeysService, |
| protected sus: SvgUtilService, |
| protected ps: PrefsService, |
| protected wss: WebSocketService, |
| protected ts: TopologyService, |
| protected trs: TrafficService, |
| protected is: IconService, |
| private lion: LionService, |
| private layout: LayoutService, |
| protected ar: ActivatedRoute, |
| @Inject('Window') public window: any, |
| ) { |
| if (this.lion.ubercache.length === 0) { |
| this.lionFn = this.dummyLion; |
| this.lion.loadCbs.set('topo-toolbar', () => this.doLion()); |
| } else { |
| this.doLion(); |
| } |
| |
| this.log.warn('Constructor', this.zoomDirective); |
| |
| this.is.loadIconDef('active'); |
| this.is.loadIconDef('bgpSpeaker'); |
| this.is.loadIconDef('bird'); |
| this.is.loadIconDef('deviceTable'); |
| this.is.loadIconDef('fiber_switch'); |
| this.is.loadIconDef('flowTable'); |
| this.is.loadIconDef('groupTable'); |
| this.is.loadIconDef('m_allTraffic'); |
| this.is.loadIconDef('m_cycleLabels'); |
| this.is.loadIconDef('m_cycleGridDisplay'); |
| this.is.loadIconDef('m_disjointPaths'); |
| this.is.loadIconDef('m_details'); |
| this.is.loadIconDef('m_endstation'); |
| this.is.loadIconDef('m_eqMaster'); |
| this.is.loadIconDef('m_fiberSwitch'); |
| this.is.loadIconDef('m_firewall'); |
| this.is.loadIconDef('m_map'); |
| this.is.loadIconDef('m_microwave'); |
| this.is.loadIconDef('m_ols'); |
| this.is.loadIconDef('m_otn'); |
| this.is.loadIconDef('m_ports'); |
| this.is.loadIconDef('m_resetZoom'); |
| this.is.loadIconDef('m_roadm'); |
| this.is.loadIconDef('m_roadm_otn'); |
| this.is.loadIconDef('m_router'); |
| this.is.loadIconDef('m_selectMap'); |
| this.is.loadIconDef('m_summary'); |
| this.is.loadIconDef('m_switch'); |
| this.is.loadIconDef('m_terminal_device'); |
| this.is.loadIconDef('m_uiAttached'); |
| this.is.loadIconDef('m_unknown'); |
| this.is.loadIconDef('meterTable'); |
| this.is.loadIconDef('microwave'); |
| this.is.loadIconDef('otn'); |
| this.is.loadIconDef('portTable'); |
| this.is.loadIconDef('roadm_otn'); |
| this.is.loadIconDef('triangleUp'); |
| this.is.loadIconDef('uiAttached'); |
| } |
| |
| /** |
| * Static functions must come before member variables |
| * @param index Corresponds to LabelToggle.Enum index |
| */ |
| private static deviceLabelFlashMessage(index: number): string { |
| switch (index) { |
| case 0: return 'fl_device_labels_hide'; |
| case 1: return 'fl_device_labels_show_friendly'; |
| case 2: return 'fl_device_labels_show_id'; |
| } |
| } |
| |
| private static hostLabelFlashMessage(index: number): string { |
| switch (index) { |
| case 0: return 'fl_host_labels_hide'; |
| case 1: return 'fl_host_labels_show_friendly'; |
| case 2: return 'fl_host_labels_show_ip'; |
| case 3: return 'fl_host_labels_show_mac'; |
| } |
| } |
| |
| private static gridDisplayFlashMessage(index: number): string { |
| switch (index) { |
| case 0: return 'fl_grid_display_hide'; |
| case 1: return 'fl_grid_display_1000'; |
| case 2: return 'fl_grid_display_geo'; |
| case 3: return 'fl_grid_display_both'; |
| } |
| } |
| |
| private static trafficTypeFlashMessage(index: number): string { |
| switch (index) { |
| case 0: return 'tr_fl_fstats_bytes'; |
| case 1: return 'tr_fl_pstats_bits'; |
| case 2: return 'tr_fl_pstats_pkts'; |
| } |
| } |
| |
| /** |
| * Pass the list of Key Commands to the KeyService, and initialize the Topology |
| * Service - which communicates with through the WebSocket to the ONOS server |
| * to get the nodes and links. |
| */ |
| ngOnInit() { |
| this.bindCommands(); |
| |
| // The components from the template are handed over to TopologyService here |
| // so that WebSocket responses can be passed back in to them |
| // The handling of the WebSocket call is delegated out to the Topology |
| // Service just to compartmentalize things a bit |
| this.ts.init(this.instance, this.background, this.force); |
| |
| // For the 2.1 release to not listen to updates of prefs as they are |
| // only the echo of what we have sent down and the event mechanism |
| // does not discern between users. Can get confused if multiple windows open |
| // this.ps.addListener((data) => this.prefsUpdateHandler(data)); |
| |
| this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState); |
| this.mapIdState = this.ps.getPrefs(TOPO_MAPID_PREFS, this.mapIdState); |
| this.trs.init(this.force); |
| |
| // Scale the window initially - then after resize |
| const zoomMapExtents = ZoomUtils.zoomToWindowSize( |
| this.bannerHeight, this.window.innerWidth, this.window.innerHeight); |
| this.zoomDirective.changeZoomLevel(zoomMapExtents, true); |
| |
| // TODO find out why the following is never printed |
| this.log.debug('TopologyComponent initialized,', |
| this.bannerHeight, this.window.innerWidth, this.window.innerHeight, |
| zoomMapExtents); |
| } |
| |
| ngAfterViewInit(): void { |
| this.ar.queryParams.subscribe(params => { |
| const intentId = params['intentId']; |
| const intentType = params['intentType']; |
| const appId = params['appId']; |
| const appName = params['appName']; |
| |
| if (intentId && intentType && appId) { |
| const selectedIntent = <Intent>{ |
| key: intentId, |
| type: intentType, |
| appId: appId, |
| appName: appName, |
| }; |
| this.ts.setSelectedIntent(selectedIntent); |
| |
| this.log.warn('TopologyComponent init with Intent: ', selectedIntent, params); |
| } |
| }); |
| } |
| |
| /** |
| * Callback function that's called whenever new Prefs are received from WebSocket |
| * |
| * Note: At present the backend server does not filter updated by logged in user, |
| * so you might get updates pertaining to a different user |
| */ |
| prefsUpdateHandler(data: any): void { |
| // Extract the TOPO2 prefs from it |
| if (data[TOPO2_PREFS]) { |
| this.prefsState = data[TOPO2_PREFS]; |
| } |
| this.log.debug('Updated topo2 prefs', this.prefsState, this.mapIdState); |
| } |
| |
| /** |
| * When this component is being stopped, disconnect the TopologyService from |
| * the WebSocket |
| */ |
| ngOnDestroy() { |
| this.ts.destroy(); |
| this.ps.removeListener((data) => this.prefsUpdateHandler(data)); |
| this.trs.destroy(); |
| this.log.debug('Topology component destroyed'); |
| } |
| |
| @HostListener('window:resize', ['$event']) |
| onResize(event) { |
| const zoomMapExtents = ZoomUtils.zoomToWindowSize( |
| this.bannerHeight, event.target.innerWidth, event.target.innerHeight); |
| this.zoomDirective.changeZoomLevel(zoomMapExtents, true); |
| this.log.debug('Topology window resize', |
| event.target.innerWidth, event.target.innerHeight, this.bannerHeight, zoomMapExtents); |
| } |
| |
| /** |
| * When ever a toolbar button is clicked, an event is sent up from toolbar |
| * component which is caught and passed on to here. |
| * @param name The name of the button that was clicked |
| */ |
| toolbarButtonClicked(name: string) { |
| switch (name) { |
| case INSTANCE_TOGGLE: |
| this.toggleInstancePanel(); |
| break; |
| case SUMMARY_TOGGLE: |
| this.toggleSummary(); |
| break; |
| case DETAILS_TOGGLE: |
| this.toggleDetails(); |
| break; |
| case HOSTS_TOGGLE: |
| this.toggleHosts(); |
| break; |
| case OFFLINE_TOGGLE: |
| this.toggleOfflineDevices(); |
| break; |
| case PORTS_TOGGLE: |
| this.togglePorts(); |
| break; |
| case BKGRND_TOGGLE: |
| this.toggleBackground(); |
| break; |
| case BKGRND_SELECT: |
| this.mapSelShown = !this.mapSelShown; |
| break; |
| case CYCLELABELS_BTN: |
| this.cycleDeviceLabels(); |
| break; |
| case CYCLEHOSTLABEL_BTN: |
| this.cycleHostLabels(); |
| break; |
| case CYCLEGRIDDISPLAY_BTN: |
| this.cycleGridDisplay(); |
| break; |
| case RESETZOOM_BTN: |
| this.resetZoom(); |
| break; |
| case EQMASTER_BTN: |
| this.equalizeMasters(); |
| break; |
| case CANCEL_TRAFFIC: |
| this.cancelTraffic(); |
| break; |
| case ALL_TRAFFIC: |
| this.cycleTrafficTypeDisplay(); |
| break; |
| case QUICKHELP_BTN: |
| this.ks.quickHelpShown = true; |
| break; |
| case LAYOUT_DEFAULT_BTN: |
| this.layout.changeLayout(LayoutType.LAYOUT_DEFAULT); |
| break; |
| case LAYOUT_ACCESS_BTN: |
| this.layout.changeLayout(LayoutType.LAYOUT_ACCESS); |
| break; |
| case ALARMS_TOGGLE: |
| this.toggleAlarms(); |
| break; |
| default: |
| this.log.warn('Unhandled Toolbar action', name); |
| } |
| } |
| |
| /** |
| * The list of key strokes that will be active in the Topology View. |
| * |
| * This action map is passed to the KeyService through the bindCommands() |
| * when this component is being initialized |
| * |
| * TODO - Replace this doggy doo doo (copied over from GUI-1) |
| * with something more structured |
| */ |
| actionMap() { |
| return { |
| A: [() => {this.cycleTrafficTypeDisplay(); }, this.lionFn('tr_btn_monitor_all')], |
| B: [(token) => {this.toggleBackground(token); }, this.lionFn('tbtt_tog_map')], |
| D: [(token) => {this.toggleDetails(token); }, this.lionFn('tbtt_tog_use_detail')], |
| E: [() => {this.equalizeMasters(); }, this.lionFn('tbtt_eq_master')], |
| H: [() => {this.toggleHosts(); }, this.lionFn('tbtt_tog_host')], |
| I: [(token) => {this.toggleInstancePanel(token); }, this.lionFn('tbtt_tog_instances')], |
| G: [() => {this.mapSelShown = !this.mapSelShown; }, this.lionFn('tbtt_sel_map')], |
| L: [() => {this.cycleDeviceLabels(); }, this.lionFn('tbtt_cyc_dev_labs')], |
| M: [() => {this.toggleOfflineDevices(); }, this.lionFn('tbtt_tog_offline')], |
| O: [() => {this.toggleSummary(); }, this.lionFn('tbtt_tog_summary')], |
| P: [(token) => {this.togglePorts(token); }, this.lionFn('tbtt_tog_porthi')], |
| Q: [() => {this.cycleGridDisplay(); }, this.lionFn('tbtt_cyc_grid_display')], |
| R: [() => {this.resetZoom(); }, this.lionFn('tbtt_reset_zoom')], |
| U: [() => {this.unpinOrFreezeNodes(); }, this.lionFn('tbtt_unpin_node')], |
| X: [() => {this.resetNodeLocation(); }, this.lionFn('tbtt_reset_loc')], |
| dot: [() => {this.toggleToolbar(); }, this.lionFn('tbtt_tog_toolbar')], |
| 0: [() => {this.cancelTraffic(); }, this.lionFn('tr_btn_cancel_monitoring')], |
| 'shift-L': [() => {this.cycleHostLabels(); }, this.lionFn('tbtt_cyc_host_labs')], |
| |
| // -- instance color palette debug |
| 9: () => { |
| this.sus.cat7().testCard(d3.select('svg#topo2')); |
| }, |
| |
| esc: [() => {this.handleEscape(); }, this.lionFn('qh_hint_esc')], |
| |
| // TODO update after adding in Background Service |
| // topology overlay selections |
| // F1: function () { t2tbs.fnKey(0); }, |
| // F2: function () { t2tbs.fnKey(1); }, |
| // F3: function () { t2tbs.fnKey(2); }, |
| // F4: function () { t2tbs.fnKey(3); }, |
| // F5: function () { t2tbs.fnKey(4); }, |
| // |
| // _keyListener: t2tbs.keyListener.bind(t2tbs), |
| |
| _helpFormat: [ |
| ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'], |
| ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'], |
| [], // this column reserved for overlay actions |
| ], |
| }; |
| } |
| |
| |
| bindCommands(additional?: any) { |
| |
| const am = this.actionMap(); |
| this.ks.keyBindings(am); |
| |
| this.ks.gestureNotes([ |
| ['click', 'Select the item and show details'], |
| ['shift-click', 'Toggle selection state'], |
| ['drag', 'Reposition (and pin) device / host'], |
| ['cmd-scroll', 'Zoom in / out'], |
| ['cmd-drag', 'Pan'], |
| ]); |
| } |
| |
| handleEscape() { |
| |
| if (false) { |
| // TODO: Cancel show mastership |
| // TODO: Cancel Active overlay |
| // TODO: Reinstate with components |
| } else { |
| this.nodeSelected(undefined); |
| this.log.debug('Handling escape'); |
| // } else if (t2rs.deselectAllNodes()) { |
| // // else if we have node selections, deselect them all |
| // // (work already done) |
| // } else if (t2rs.deselectLink()) { |
| // // else if we have a link selection, deselect it |
| // // (work already done) |
| // } else if (t2is.isVisible()) { |
| // // If the instance panel is visible, close it |
| // t2is.toggle(); |
| // } else if (t2sp.isVisible()) { |
| // // If the summary panel is visible, close it |
| // t2sp.toggle(); |
| } |
| } |
| |
| /** |
| * Updates the cache of preferences locally and onwards to the PrefsService |
| * @param what The attribute of the local topo2-prefs cache to update |
| * @param b the value to update it with |
| */ |
| updatePrefsState(what: string, b: number) { |
| this.prefsState[what] = b; |
| this.ps.setPrefs(TOPO2_PREFS, this.prefsState); |
| } |
| |
| /** |
| * When the button is clicked on the toolbar or the L key is pressed |
| * 1) cycle through options |
| * 2) flash up a message |
| * 3a) Update the local prefs cache |
| * 3b) And passes on to the global prefs service which sends back to the server |
| * 3c) It also has a knock on effect of passing it on to ForceSvgComponent |
| * because prefsState.dlbls is given as an input to it |
| * 3d) This will in turn pass it down to the DeviceSvgComponent which |
| * displays the label |
| */ |
| protected cycleDeviceLabels() { |
| const old: LabelToggle.Enum = this.prefsState.dlbls; |
| const next = LabelToggle.next(old); |
| this.flashMsg = this.lionFn(TopologyComponent.deviceLabelFlashMessage(next)); |
| this.updatePrefsState(PREF_DLBLS, next); |
| this.log.debug('Cycling device labels', old, next); |
| } |
| |
| protected cycleHostLabels() { |
| const old: HostLabelToggle.Enum = this.prefsState.hlbls; |
| const next = HostLabelToggle.next(old); |
| this.flashMsg = this.lionFn(TopologyComponent.hostLabelFlashMessage(next)); |
| this.updatePrefsState(PREF_HLBLS, next); |
| this.log.debug('Cycling host labels', old, next); |
| } |
| |
| protected cycleGridDisplay() { |
| const old: GridDisplayToggle.Enum = this.prefsState.grid; |
| const next = GridDisplayToggle.next(old); |
| this.flashMsg = this.lionFn(TopologyComponent.gridDisplayFlashMessage(next)); |
| this.updatePrefsState(PREF_GRID, next); |
| this.log.debug('Cycling grid display', old, next); |
| } |
| |
| protected cycleTrafficTypeDisplay() { |
| const old: TrafficType.Enum = this.prefsState.traffic; // by number |
| const next = TrafficType.next(old); |
| this.flashMsg = this.lionFn(TopologyComponent.trafficTypeFlashMessage(next)); |
| this.updatePrefsState(PREF_TRAFFIC, next); |
| this.trs.requestTraffic(next); |
| this.log.debug('Cycling traffic display', old, next); |
| } |
| |
| /** |
| * When the button is clicked on the toolbar or the B key is pressed |
| * 1) Find the inverse of the current state (held as 1 or 0) |
| * 2) Flash up a message on screen |
| * 3b) And passes on to the global prefs service which sends back to the server |
| * 3c) It also has a knock on effect of passing it on to ToolbarComponent |
| * because prefsState.bg is given as an input to it |
| * @param token not currently used |
| */ |
| protected toggleBackground(token?: KeysToken) { |
| const bg: boolean = !Boolean(this.prefsState.bg); |
| this.flashMsg = this.lionFn(bg ? 'show' : 'hide') + |
| ' ' + this.lionFn('fl_background_map'); |
| this.updatePrefsState(PREF_BG, bg ? 1 : 0); |
| this.log.debug('Toggling background', token, bg ? 'shown' : 'hidden'); |
| } |
| |
| protected toggleDetails(token?: KeysToken) { |
| const on: boolean = !Boolean(this.prefsState.detail); |
| this.flashMsg = this.lionFn(on ? 'show' : 'hide') + |
| ' ' + this.lionFn('fl_panel_details'); |
| this.updatePrefsState(PREF_DETAIL, on ? 1 : 0); |
| this.log.debug('Toggling details', token); |
| } |
| |
| protected toggleInstancePanel(token?: KeysToken) { |
| const on: boolean = !Boolean(this.prefsState.insts); |
| this.flashMsg = this.lionFn(on ? 'show' : 'hide') + |
| ' ' + this.lionFn('fl_panel_instances'); |
| this.updatePrefsState(PREF_INSTS, on ? 1 : 0); |
| this.log.debug('Toggling instances', token, on); |
| } |
| |
| protected toggleSummary() { |
| const on: boolean = !Boolean(this.prefsState.summary); |
| this.flashMsg = this.lionFn(on ? 'show' : 'hide') + |
| ' ' + this.lionFn('fl_panel_summary'); |
| this.updatePrefsState(PREF_SUMMARY, on ? 1 : 0); |
| } |
| |
| protected togglePorts(token?: KeysToken) { |
| const current: boolean = !Boolean(this.prefsState.porthl); |
| this.flashMsg = this.lionFn(current ? 'enable' : 'disable') + |
| ' ' + this.lionFn('fl_port_highlighting'); |
| this.updatePrefsState(PREF_PORTHL, current ? 1 : 0); |
| this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting'); |
| } |
| |
| protected toggleToolbar() { |
| const on: boolean = !Boolean(this.prefsState.toolbar); |
| this.updatePrefsState(PREF_TOOLBAR, on ? 1 : 0); |
| this.log.debug('toggling toolbar', on ? 'shown' : 'hidden'); |
| } |
| |
| protected toggleHosts() { |
| const current: boolean = !Boolean(this.prefsState.hosts); |
| this.flashMsg = this.lionFn('hosts') + ' ' + |
| this.lionFn(current ? 'visible' : 'hidden'); |
| this.updatePrefsState(PREF_HOSTS, current ? 1 : 0); |
| this.log.debug('toggling hosts: ', this.prefsState.hosts ? 'Show' : 'Hide'); |
| } |
| |
| protected toggleOfflineDevices() { |
| const on: boolean = !Boolean(this.prefsState.offdev); |
| this.flashMsg = this.lionFn(on ? 'show' : 'hide') + |
| ' ' + this.lionFn('fl_offline_devices'); |
| this.updatePrefsState(PREF_OFFDEV, on ? 1 : 0); |
| this.log.debug('toggling offline devices', this.prefsState.offdev); |
| } |
| |
| |
| protected toggleAlarms() { |
| const on: boolean = !Boolean(this.prefsState.alarms); |
| this.flashMsg = this.lionFn(on ? 'show' : 'hide') + ' Alarms'; |
| this.updatePrefsState(PREF_ALARMS, on ? 1 : 0); |
| this.log.debug('Alarms toggled', on); |
| } |
| |
| protected resetZoom() { |
| const zoomMapExtents = ZoomUtils.zoomToWindowSize( |
| this.bannerHeight, this.window.innerWidth, this.window.innerHeight); |
| this.zoomDirective.changeZoomLevel(zoomMapExtents, false); |
| this.flashMsg = this.lionFn('fl_pan_zoom_reset'); |
| } |
| |
| protected equalizeMasters() { |
| this.wss.sendEvent('equalizeMasters', {}); |
| this.flashMsg = this.lionFn('fl_eq_masters'); |
| this.log.debug('equalizing masters'); |
| } |
| |
| /** |
| * If any nodes with fixed positions had been dragged out of place |
| * then put back where they belong |
| * If there are some devices selected reset only these |
| */ |
| protected resetNodeLocation() { |
| const numNodes = this.force.resetNodeLocations(); |
| this.flashMsg = this.lionFn('fl_reset_node_locations') + |
| '(' + String(numNodes) + ')'; |
| this.log.debug('resetting ', numNodes, 'node(s) location'); |
| } |
| |
| /** |
| * Toggle floating nodes between pinned and frozen |
| * If there are floating nodes selected toggle only these |
| */ |
| protected unpinOrFreezeNodes() { |
| const pinned: boolean = !Boolean(this.prefsState.pinned); |
| const numNodes = this.force.unpinOrFreezeNodes(pinned); |
| this.flashMsg = this.lionFn(pinned ? |
| 'fl_pinned_floating_nodes' : 'fl_unpinned_floating_nodes') + |
| '(' + String(numNodes) + ')'; |
| this.updatePrefsState(PREF_PINNED, pinned ? 1 : 0); |
| this.log.debug('Toggling pinning for floating ', numNodes, 'nodes', pinned); |
| } |
| |
| /** |
| * Check to see if this is needed anymore |
| * @param what - a key stroke |
| */ |
| protected notValid(what) { |
| this.log.warn('topo.js getActionEntry(): Not a valid ' + what); |
| } |
| |
| /** |
| * Check to see if this is needed anymore |
| * @param key - a key stroke |
| */ |
| getActionEntry(key) { |
| let entry; |
| |
| if (!key) { |
| this.notValid('key'); |
| return null; |
| } |
| |
| entry = this.actionMap()[key]; |
| |
| if (!entry) { |
| this.notValid('actionMap (' + key + ') entry'); |
| return null; |
| } |
| return this.fs.isA(entry) || [entry, '']; |
| } |
| |
| /** |
| * An event handler that updates the details panel as items are |
| * selected in the forcesvg layer |
| * |
| * @param nodesOrLink the item(s) to display details of |
| */ |
| nodeSelected(nodesOrLink: UiElement[]) { |
| this.details.ngOnChanges({'selectedNodes': |
| new SimpleChange(undefined, nodesOrLink, true)}); |
| } |
| |
| /** |
| * Cancel traffic monitoring |
| */ |
| cancelTraffic() { |
| this.flashMsg = this.lionFn('fl_monitoring_canceled'); |
| this.trs.cancelTraffic(); |
| } |
| |
| changeMap(map: MapObject) { |
| this.mapSelShown = false; // Hide the MapSelector component |
| this.mapIdState = map; |
| this.ps.setPrefs(TOPO_MAPID_PREFS, this.mapIdState); |
| this.log.debug('Map has been changed to ', map); |
| } |
| |
| mapExtentsZoom(zoomMapExtents: TopoZoomPrefs) { |
| // this.zoomDirective.updateZoomState(zoomPrefs.tx, zoomPrefs.ty, zoomPrefs.sc); |
| this.zoomDirective.changeZoomLevel(zoomMapExtents); |
| this.log.debug('Map zoom prefs updated', zoomMapExtents); |
| } |
| |
| backgroundClicked(event: MouseEvent) { |
| const elemTagName = event.target['tagName'] + ' ' + event.target['id']; |
| if (BACKGROUND_ELEMENTS.includes(elemTagName)) { |
| this.force.updateSelected( |
| <SelectedEvent>{ |
| uiElement: undefined, |
| deselecting: true |
| } |
| ); |
| } |
| this.ts.cancelHighlights(); |
| this.force.cancelAllLinkHighlightsNow(); |
| } |
| |
| /** |
| * Read the LION bundle for Toolbar and set up the lionFn |
| */ |
| doLion() { |
| this.lionFn = this.lion.bundle('core.view.Topo'); |
| } |
| |
| /** |
| * A dummy implementation of the lionFn until the response is received and the LION |
| * bundle is received from the WebSocket |
| */ |
| dummyLion(key: string): string { |
| return '%' + key + '%'; |
| } |
| } |