blob: 8c6b95390d2ccef41138d7f49c0eac0941072ffa [file] [log] [blame]
Sean Condonf4f54a12018-10-10 23:25:46 +01001/*
Sean Condon91481822019-01-01 13:56:14 +00002 * Copyright 2019-present Open Networking Foundation
Sean Condonf4f54a12018-10-10 23:25:46 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the 'License');
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an 'AS IS' BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Sean Condon0c577f62018-11-18 22:40:05 +000016import {
Sean Condonff85fbe2019-03-16 14:28:46 +000017 AfterContentInit,
18 Component, HostListener, Inject, Input,
Sean Condon0c577f62018-11-18 22:40:05 +000019 OnDestroy,
Sean Condon021f0fa2018-12-06 23:31:11 -080020 OnInit, SimpleChange,
Sean Condon0c577f62018-11-18 22:40:05 +000021 ViewChild
22} from '@angular/core';
Sean Condon55c30532018-10-29 12:26:57 +000023import * as d3 from 'd3';
Sean Condonf4f54a12018-10-10 23:25:46 +010024import {
Sean Condon28884332019-03-21 14:07:00 +000025 FnService,
26 IconService,
Sean Condon0c577f62018-11-18 22:40:05 +000027 KeysService,
Sean Condon28884332019-03-21 14:07:00 +000028 KeysToken,
29 LionService,
Sean Condon0c577f62018-11-18 22:40:05 +000030 LogService,
31 PrefsService,
32 SvgUtilService,
Sean Condon28884332019-03-21 14:07:00 +000033 TopoZoomPrefs,
Sean Condon0c577f62018-11-18 22:40:05 +000034 WebSocketService,
Sean Condon28884332019-03-21 14:07:00 +000035 ZoomUtils
Sean Condonf4f54a12018-10-10 23:25:46 +010036} from 'gui2-fw-lib';
Sean Condon0c577f62018-11-18 22:40:05 +000037import {InstanceComponent} from '../panel/instance/instance.component';
Sean Condon0c577f62018-11-18 22:40:05 +000038import {DetailsComponent} from '../panel/details/details.component';
39import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component';
40import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
41import {TopologyService} from '../topology.service';
Sean Condon91481822019-01-01 13:56:14 +000042import {
Sean Condon71910542019-02-16 18:16:42 +000043 GridDisplayToggle,
Sean Condon91481822019-01-01 13:56:14 +000044 HostLabelToggle,
45 LabelToggle,
46 UiElement
47} from '../layer/forcesvg/models';
Sean Condonb2c483c2019-01-16 20:28:55 +000048import {
Sean Condon28884332019-03-21 14:07:00 +000049 ALL_TRAFFIC,
50 BKGRND_SELECT,
51 BKGRND_TOGGLE,
52 CANCEL_TRAFFIC,
53 CYCLEGRIDDISPLAY_BTN,
54 CYCLEHOSTLABEL_BTN,
55 CYCLELABELS_BTN,
56 DETAILS_TOGGLE,
57 EQMASTER_BTN,
58 HOSTS_TOGGLE,
59 INSTANCE_TOGGLE,
60 LAYOUT_ACCESS_BTN,
61 LAYOUT_DEFAULT_BTN,
62 OFFLINE_TOGGLE,
63 PORTS_TOGGLE,
64 QUICKHELP_BTN,
65 RESETZOOM_BTN,
66 SUMMARY_TOGGLE
Sean Condonb2c483c2019-01-16 20:28:55 +000067} from '../panel/toolbar/toolbar.component';
Sean Condon19e83672019-04-13 16:21:52 +010068import {TrafficService, TrafficType} from '../traffic.service';
Sean Condon91481822019-01-01 13:56:14 +000069import {ZoomableDirective} from '../layer/zoomable.directive';
Sean Condon0d064ec2019-02-04 21:53:53 +000070import {MapObject} from '../layer/maputils';
Sean Condon28884332019-03-21 14:07:00 +000071import {LayoutService, LayoutType} from '../layout.service';
Sean Condon64ea7d22019-04-12 19:39:13 +010072import {SelectedEvent} from '../layer/forcesvg/visuals/nodevisual';
Sean Condonf4f54a12018-10-10 23:25:46 +010073
Sean Condonb2c483c2019-01-16 20:28:55 +000074const TOPO2_PREFS = 'topo2_prefs';
Sean Condon0d064ec2019-02-04 21:53:53 +000075const TOPO_MAPID_PREFS = 'topo_mapid';
76
Sean Condonb2c483c2019-01-16 20:28:55 +000077const PREF_BG = 'bg';
78const PREF_DETAIL = 'detail';
79const PREF_DLBLS = 'dlbls';
80const PREF_HLBLS = 'hlbls';
Sean Condon71910542019-02-16 18:16:42 +000081const PREF_GRID = 'grid';
Sean Condonb2c483c2019-01-16 20:28:55 +000082const PREF_HOSTS = 'hosts';
83const PREF_INSTS = 'insts';
84const PREF_OFFDEV = 'offdev';
85const PREF_PORTHL = 'porthl';
86const PREF_SUMMARY = 'summary';
87const PREF_TOOLBAR = 'toolbar';
Sean Condon9de21352019-04-06 19:22:27 +010088const PREF_PINNED = 'pinned';
Sean Condon19e83672019-04-13 16:21:52 +010089const PREF_TRAFFIC = 'traffic';
Sean Condonb2c483c2019-01-16 20:28:55 +000090
Sean Condon64ea7d22019-04-12 19:39:13 +010091const BACKGROUND_ELEMENTS = [
92 'svg topo2',
93 'path bgmap'
94];
95
Sean Condonb2c483c2019-01-16 20:28:55 +000096/**
Sean Condon0d064ec2019-02-04 21:53:53 +000097 * Model of the topo2_prefs object - this is a subset of the overall Prefs returned
Sean Condonb2c483c2019-01-16 20:28:55 +000098 * by the server
99 */
100export interface Topo2Prefs {
101 bg: number;
102 detail: number;
103 dlbls: number;
104 hlbls: number;
105 hosts: number;
106 insts: number;
107 offdev: number;
108 porthl: number;
109 spr: number;
110 ovid: string;
111 summary: number;
112 toolbar: number;
Sean Condon71910542019-02-16 18:16:42 +0000113 grid: number;
Sean Condon9de21352019-04-06 19:22:27 +0100114 pinned: number;
Sean Condon19e83672019-04-13 16:21:52 +0100115 traffic: number;
Sean Condonb2c483c2019-01-16 20:28:55 +0000116}
117
Sean Condonf4f54a12018-10-10 23:25:46 +0100118/**
119 * ONOS GUI Topology View
120 *
121 * This Topology View component is the top level component in a hierarchy that
122 * comprises the whole Topology View
123 *
124 * There are three main parts (panels, graphical and breadcrumbs)
125 * The panel hierarchy
126 * |-- Instances Panel (shows ONOS instances)
127 * |-- Summary Panel (summary of ONOS)
128 * |-- Toolbar Panel (the toolbar)
129 * |-- Details Panel (when a node is selected in the Force graphical view (see below))
130 *
131 * The graphical hierarchy contains
132 * Topology (this)
133 * |-- No Devices Connected (only of there are no nodes to show)
134 * |-- Zoom Layer (everything beneath this can be zoomed and panned)
135 * |-- Background (container for any backgrounds - can be toggled on and off)
136 * |-- Map
137 * |-- Forces (all of the nodes and links laid out by a d3.force simulation)
138 *
139 * The breadcrumbs
140 * |-- Breadcrumb (in region view a way of navigating back up through regions)
141 */
142@Component({
143 selector: 'onos-topology',
144 templateUrl: './topology.component.html',
145 styleUrls: ['./topology.component.css']
146})
Sean Condonff85fbe2019-03-16 14:28:46 +0000147export class TopologyComponent implements AfterContentInit, OnInit, OnDestroy {
148 @Input() bannerHeight: number = 48;
Sean Condonaa4366d2018-11-02 14:29:01 +0000149 // These are references to the components inserted in the template
Sean Condonf4f54a12018-10-10 23:25:46 +0100150 @ViewChild(InstanceComponent) instance: InstanceComponent;
Sean Condonf4f54a12018-10-10 23:25:46 +0100151 @ViewChild(DetailsComponent) details: DetailsComponent;
Sean Condonaa4366d2018-11-02 14:29:01 +0000152 @ViewChild(BackgroundSvgComponent) background: BackgroundSvgComponent;
153 @ViewChild(ForceSvgComponent) force: ForceSvgComponent;
Sean Condon91481822019-01-01 13:56:14 +0000154 @ViewChild(ZoomableDirective) zoomDirective: ZoomableDirective;
Sean Condonf4f54a12018-10-10 23:25:46 +0100155
156 flashMsg: string = '';
Sean Condonb2c483c2019-01-16 20:28:55 +0000157 // These are used as defaults if nothing is set on the server
158 prefsState: Topo2Prefs = <Topo2Prefs>{
159 bg: 0,
160 detail: 1,
161 dlbls: 0,
162 hlbls: 2,
163 hosts: 0,
164 insts: 1,
165 offdev: 1,
166 ovid: 'traffic', // default to traffic overlay
167 porthl: 1,
168 spr: 0,
169 summary: 1,
170 toolbar: 0,
Sean Condon19e83672019-04-13 16:21:52 +0100171 grid: 0,
172 pinned: 0,
173 traffic: 2 // default to PORTSTATSPKTSEC, as it will iterate over to 0 on init
Sean Condonb2c483c2019-01-16 20:28:55 +0000174 };
Sean Condon0d064ec2019-02-04 21:53:53 +0000175
176 mapIdState: MapObject = <MapObject>{
177 id: undefined,
178 scale: 1.0
179 };
180 mapSelShown: boolean = false;
Sean Condon91481822019-01-01 13:56:14 +0000181 lionFn; // Function
Sean Condon55c30532018-10-29 12:26:57 +0000182
Sean Condon71910542019-02-16 18:16:42 +0000183 gridShown: boolean = true;
184 geoGridShown: boolean = true;
185
Sean Condonf4f54a12018-10-10 23:25:46 +0100186 constructor(
187 protected log: LogService,
188 protected fs: FnService,
189 protected ks: KeysService,
190 protected sus: SvgUtilService,
191 protected ps: PrefsService,
Sean Condon55c30532018-10-29 12:26:57 +0000192 protected wss: WebSocketService,
Sean Condonaa4366d2018-11-02 14:29:01 +0000193 protected ts: TopologyService,
Sean Condon91481822019-01-01 13:56:14 +0000194 protected trs: TrafficService,
195 protected is: IconService,
196 private lion: LionService,
Sean Condon28884332019-03-21 14:07:00 +0000197 private layout: LayoutService,
Sean Condonff85fbe2019-03-16 14:28:46 +0000198 @Inject('Window') public window: any,
Sean Condonf4f54a12018-10-10 23:25:46 +0100199 ) {
Sean Condon91481822019-01-01 13:56:14 +0000200 if (this.lion.ubercache.length === 0) {
201 this.lionFn = this.dummyLion;
202 this.lion.loadCbs.set('topo-toolbar', () => this.doLion());
203 } else {
204 this.doLion();
205 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100206
Sean Condon91481822019-01-01 13:56:14 +0000207 this.is.loadIconDef('bird');
208 this.is.loadIconDef('active');
209 this.is.loadIconDef('uiAttached');
210 this.is.loadIconDef('m_switch');
211 this.is.loadIconDef('m_roadm');
212 this.is.loadIconDef('m_router');
213 this.is.loadIconDef('m_uiAttached');
214 this.is.loadIconDef('m_endstation');
215 this.is.loadIconDef('m_ports');
216 this.is.loadIconDef('m_summary');
217 this.is.loadIconDef('m_details');
218 this.is.loadIconDef('m_map');
Sean Condon0d064ec2019-02-04 21:53:53 +0000219 this.is.loadIconDef('m_selectMap');
Sean Condon91481822019-01-01 13:56:14 +0000220 this.is.loadIconDef('m_cycleLabels');
Sean Condon71910542019-02-16 18:16:42 +0000221 this.is.loadIconDef('m_cycleGridDisplay');
Sean Condon91481822019-01-01 13:56:14 +0000222 this.is.loadIconDef('m_resetZoom');
223 this.is.loadIconDef('m_eqMaster');
224 this.is.loadIconDef('m_unknown');
225 this.is.loadIconDef('m_allTraffic');
226 this.is.loadIconDef('deviceTable');
227 this.is.loadIconDef('flowTable');
228 this.is.loadIconDef('portTable');
229 this.is.loadIconDef('groupTable');
230 this.is.loadIconDef('meterTable');
231 this.is.loadIconDef('triangleUp');
Sean Condon28884332019-03-21 14:07:00 +0000232 this.is.loadIconDef('m_disjointPaths');
233 this.is.loadIconDef('m_fiberSwitch');
Sean Condonf4f54a12018-10-10 23:25:46 +0100234 this.log.debug('Topology component constructed');
235 }
236
Sean Condon91481822019-01-01 13:56:14 +0000237 /**
238 * Static functions must come before member variables
Sean Condonff85fbe2019-03-16 14:28:46 +0000239 * @param index Corresponds to LabelToggle.Enum index
Sean Condon91481822019-01-01 13:56:14 +0000240 */
Sean Condon021f0fa2018-12-06 23:31:11 -0800241 private static deviceLabelFlashMessage(index: number): string {
242 switch (index) {
Sean Condon91481822019-01-01 13:56:14 +0000243 case 0: return 'fl_device_labels_hide';
244 case 1: return 'fl_device_labels_show_friendly';
245 case 2: return 'fl_device_labels_show_id';
Sean Condon021f0fa2018-12-06 23:31:11 -0800246 }
247 }
248
249 private static hostLabelFlashMessage(index: number): string {
250 switch (index) {
Sean Condon91481822019-01-01 13:56:14 +0000251 case 0: return 'fl_host_labels_hide';
252 case 1: return 'fl_host_labels_show_friendly';
253 case 2: return 'fl_host_labels_show_ip';
254 case 3: return 'fl_host_labels_show_mac';
Sean Condon021f0fa2018-12-06 23:31:11 -0800255 }
256 }
257
Sean Condon71910542019-02-16 18:16:42 +0000258 private static gridDisplayFlashMessage(index: number): string {
259 switch (index) {
260 case 0: return 'fl_grid_display_hide';
261 case 1: return 'fl_grid_display_1000';
262 case 2: return 'fl_grid_display_geo';
263 case 3: return 'fl_grid_display_both';
264 }
265 }
266
Sean Condon19e83672019-04-13 16:21:52 +0100267 private static trafficTypeFlashMessage(index: number): string {
268 switch (index) {
269 case 0: return 'tr_fl_fstats_bytes';
270 case 1: return 'tr_fl_pstats_bits';
271 case 2: return 'tr_fl_pstats_pkts';
272 }
273 }
274
Sean Condon91481822019-01-01 13:56:14 +0000275 /**
276 * Pass the list of Key Commands to the KeyService, and initialize the Topology
277 * Service - which communicates with through the WebSocket to the ONOS server
278 * to get the nodes and links.
279 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100280 ngOnInit() {
281 this.bindCommands();
Sean Condonaa4366d2018-11-02 14:29:01 +0000282 // The components from the template are handed over to TopologyService here
283 // so that WebSocket responses can be passed back in to them
284 // The handling of the WebSocket call is delegated out to the Topology
285 // Service just to compartmentalize things a bit
286 this.ts.init(this.instance, this.background, this.force);
Sean Condonb2c483c2019-01-16 20:28:55 +0000287
288 this.ps.addListener((data) => this.prefsUpdateHandler(data));
289 this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState);
Sean Condon0d064ec2019-02-04 21:53:53 +0000290 this.mapIdState = this.ps.getPrefs(TOPO_MAPID_PREFS, this.mapIdState);
Sean Condon19e83672019-04-13 16:21:52 +0100291 this.trs.init(this.force);
Sean Condonff85fbe2019-03-16 14:28:46 +0000292
Sean Condonf4f54a12018-10-10 23:25:46 +0100293 this.log.debug('Topology component initialized');
294 }
295
Sean Condonff85fbe2019-03-16 14:28:46 +0000296 ngAfterContentInit(): void {
297 // Scale the window initially - then after resize
298 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
299 this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
300 this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
301 this.log.debug('Topology zoom initialized',
302 this.bannerHeight, this.window.innerWidth, this.window.innerHeight,
303 zoomMapExtents);
304 }
305
Sean Condon91481822019-01-01 13:56:14 +0000306 /**
Sean Condonb2c483c2019-01-16 20:28:55 +0000307 * Callback function that's called whenever new Prefs are received from WebSocket
308 *
309 * Note: At present the backend server does not filter updated by logged in user,
310 * so you might get updates pertaining to a different user
311 */
312 prefsUpdateHandler(data: any): void {
313 // Extract the TOPO2 prefs from it
Sean Condon0d064ec2019-02-04 21:53:53 +0000314 if (data[TOPO2_PREFS]) {
315 this.prefsState = data[TOPO2_PREFS];
316 }
Sean Condon0d064ec2019-02-04 21:53:53 +0000317 this.log.debug('Updated topo2 prefs', this.prefsState, this.mapIdState);
Sean Condonb2c483c2019-01-16 20:28:55 +0000318 }
319
320 /**
Sean Condon91481822019-01-01 13:56:14 +0000321 * When this component is being stopped, disconnect the TopologyService from
322 * the WebSocket
323 */
Sean Condonaa4366d2018-11-02 14:29:01 +0000324 ngOnDestroy() {
325 this.ts.destroy();
Sean Condonb2c483c2019-01-16 20:28:55 +0000326 this.ps.removeListener((data) => this.prefsUpdateHandler(data));
Sean Condon19e83672019-04-13 16:21:52 +0100327 this.trs.destroy();
Sean Condonaa4366d2018-11-02 14:29:01 +0000328 this.log.debug('Topology component destroyed');
329 }
330
Sean Condonff85fbe2019-03-16 14:28:46 +0000331 @HostListener('window:resize', ['$event'])
332 onResize(event) {
333 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
334 this.bannerHeight, event.target.innerWidth, event.target.innerHeight);
335 this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
336 this.log.debug('Topology window resize',
337 event.target.innerWidth, event.target.innerHeight, this.bannerHeight, zoomMapExtents);
338 }
339
Sean Condon91481822019-01-01 13:56:14 +0000340 /**
341 * When ever a toolbar button is clicked, an event is sent up from toolbar
342 * component which is caught and passed on to here.
343 * @param name The name of the button that was clicked
344 */
345 toolbarButtonClicked(name: string) {
346 switch (name) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000347 case INSTANCE_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000348 this.toggleInstancePanel();
349 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000350 case SUMMARY_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000351 this.toggleSummary();
352 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000353 case DETAILS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000354 this.toggleDetails();
355 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000356 case HOSTS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000357 this.toggleHosts();
358 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000359 case OFFLINE_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000360 this.toggleOfflineDevices();
361 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000362 case PORTS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000363 this.togglePorts();
364 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000365 case BKGRND_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000366 this.toggleBackground();
367 break;
Sean Condon0d064ec2019-02-04 21:53:53 +0000368 case BKGRND_SELECT:
369 this.mapSelShown = !this.mapSelShown;
370 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000371 case CYCLELABELS_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000372 this.cycleDeviceLabels();
373 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000374 case CYCLEHOSTLABEL_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000375 this.cycleHostLabels();
376 break;
Sean Condon71910542019-02-16 18:16:42 +0000377 case CYCLEGRIDDISPLAY_BTN:
378 this.cycleGridDisplay();
379 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000380 case RESETZOOM_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000381 this.resetZoom();
382 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000383 case EQMASTER_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000384 this.equalizeMasters();
385 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000386 case CANCEL_TRAFFIC:
Sean Condon91481822019-01-01 13:56:14 +0000387 this.cancelTraffic();
388 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000389 case ALL_TRAFFIC:
Sean Condon19e83672019-04-13 16:21:52 +0100390 this.cycleTrafficTypeDisplay();
Sean Condon91481822019-01-01 13:56:14 +0000391 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000392 case QUICKHELP_BTN:
393 this.ks.quickHelpShown = true;
394 break;
Sean Condon28884332019-03-21 14:07:00 +0000395 case LAYOUT_DEFAULT_BTN:
396 this.layout.changeLayout(LayoutType.LAYOUT_DEFAULT);
397 break;
398 case LAYOUT_ACCESS_BTN:
399 this.layout.changeLayout(LayoutType.LAYOUT_ACCESS);
400 break;
Sean Condon91481822019-01-01 13:56:14 +0000401 default:
402 this.log.warn('Unhandled Toolbar action', name);
403 }
404 }
405
406 /**
407 * The list of key strokes that will be active in the Topology View.
408 *
409 * This action map is passed to the KeyService through the bindCommands()
410 * when this component is being initialized
Sean Condonbed2e032019-04-17 22:22:49 +0100411 *
412 * TODO - Replace this doggy doo doo (copied over from GUI-1)
413 * with something more structured
Sean Condon91481822019-01-01 13:56:14 +0000414 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100415 actionMap() {
416 return {
Sean Condonbed2e032019-04-17 22:22:49 +0100417 A: [() => {this.cycleTrafficTypeDisplay(); }, this.lionFn('tr_btn_monitor_all')],
418 B: [(token) => {this.toggleBackground(token); }, this.lionFn('tbtt_tog_map')],
419 D: [(token) => {this.toggleDetails(token); }, this.lionFn('tbtt_tog_use_detail')],
420 E: [() => {this.equalizeMasters(); }, this.lionFn('tbtt_eq_master')],
421 H: [() => {this.toggleHosts(); }, this.lionFn('tbtt_tog_host')],
422 I: [(token) => {this.toggleInstancePanel(token); }, this.lionFn('tbtt_tog_instances')],
423 G: [() => {this.mapSelShown = !this.mapSelShown; }, this.lionFn('tbtt_sel_map')],
424 L: [() => {this.cycleDeviceLabels(); }, this.lionFn('tbtt_cyc_dev_labs')],
425 M: [() => {this.toggleOfflineDevices(); }, this.lionFn('tbtt_tog_offline')],
426 O: [() => {this.toggleSummary(); }, this.lionFn('tbtt_tog_summary')],
427 P: [(token) => {this.togglePorts(token); }, this.lionFn('tbtt_tog_porthi')],
428 Q: [() => {this.cycleGridDisplay(); }, this.lionFn('tbtt_cyc_grid_display')],
429 R: [() => {this.resetZoom(); }, this.lionFn('tbtt_reset_zoom')],
430 U: [() => {this.unpinOrFreezeNodes(); }, this.lionFn('tbtt_unpin_node')],
431 X: [() => {this.resetNodeLocation(); }, this.lionFn('tbtt_reset_loc')],
432 dot: [() => {this.toggleToolbar(); }, this.lionFn('tbtt_tog_toolbar')],
433 0: [() => {this.cancelTraffic(); }, this.lionFn('tr_btn_cancel_monitoring')],
434 'shift-L': [() => {this.cycleHostLabels(); }, this.lionFn('tbtt_cyc_host_labs')],
Sean Condonf4f54a12018-10-10 23:25:46 +0100435
436 // -- instance color palette debug
Sean Condon55c30532018-10-29 12:26:57 +0000437 9: () => {
438 this.sus.cat7().testCard(d3.select('svg#topo2'));
Sean Condonf4f54a12018-10-10 23:25:46 +0100439 },
440
Sean Condonbed2e032019-04-17 22:22:49 +0100441 esc: [() => {this.handleEscape(); }, this.lionFn('qh_hint_esc')],
Sean Condonf4f54a12018-10-10 23:25:46 +0100442
443 // TODO update after adding in Background Service
444 // topology overlay selections
445 // F1: function () { t2tbs.fnKey(0); },
446 // F2: function () { t2tbs.fnKey(1); },
447 // F3: function () { t2tbs.fnKey(2); },
448 // F4: function () { t2tbs.fnKey(3); },
449 // F5: function () { t2tbs.fnKey(4); },
450 //
451 // _keyListener: t2tbs.keyListener.bind(t2tbs),
452
453 _helpFormat: [
454 ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
455 ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
456 [], // this column reserved for overlay actions
457 ],
458 };
459 }
460
461
462 bindCommands(additional?: any) {
463
464 const am = this.actionMap();
465 const add = this.fs.isO(additional);
466
Sean Condonf4f54a12018-10-10 23:25:46 +0100467 this.ks.keyBindings(am);
468
469 this.ks.gestureNotes([
470 ['click', 'Select the item and show details'],
471 ['shift-click', 'Toggle selection state'],
472 ['drag', 'Reposition (and pin) device / host'],
473 ['cmd-scroll', 'Zoom in / out'],
474 ['cmd-drag', 'Pan'],
475 ]);
476 }
477
478 handleEscape() {
479
480 if (false) {
481 // TODO: Cancel show mastership
482 // TODO: Cancel Active overlay
483 // TODO: Reinstate with components
484 } else {
Sean Condonb2c483c2019-01-16 20:28:55 +0000485 this.nodeSelected(undefined);
Sean Condonf4f54a12018-10-10 23:25:46 +0100486 this.log.debug('Handling escape');
487 // } else if (t2rs.deselectAllNodes()) {
488 // // else if we have node selections, deselect them all
489 // // (work already done)
490 // } else if (t2rs.deselectLink()) {
491 // // else if we have a link selection, deselect it
492 // // (work already done)
493 // } else if (t2is.isVisible()) {
494 // // If the instance panel is visible, close it
495 // t2is.toggle();
496 // } else if (t2sp.isVisible()) {
497 // // If the summary panel is visible, close it
498 // t2sp.toggle();
499 }
500 }
501
Sean Condonb2c483c2019-01-16 20:28:55 +0000502 /**
503 * Updates the cache of preferences locally and onwards to the PrefsService
504 * @param what The attribute of the local topo2-prefs cache to update
505 * @param b the value to update it with
506 */
507 updatePrefsState(what: string, b: number) {
508 this.prefsState[what] = b;
509 this.ps.setPrefs(TOPO2_PREFS, this.prefsState);
Sean Condonf4f54a12018-10-10 23:25:46 +0100510 }
511
Sean Condonb2c483c2019-01-16 20:28:55 +0000512 /**
513 * When the button is clicked on the toolbar or the L key is pressed
514 * 1) cycle through options
515 * 2) flash up a message
516 * 3a) Update the local prefs cache
517 * 3b) And passes on to the global prefs service which sends back to the server
518 * 3c) It also has a knock on effect of passing it on to ForceSvgComponent
519 * because prefsState.dlbls is given as an input to it
520 * 3d) This will in turn pass it down to the DeviceSvgComponent which
521 * displays the label
522 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100523 protected cycleDeviceLabels() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000524 const old: LabelToggle.Enum = this.prefsState.dlbls;
Sean Condon021f0fa2018-12-06 23:31:11 -0800525 const next = LabelToggle.next(old);
Sean Condon91481822019-01-01 13:56:14 +0000526 this.flashMsg = this.lionFn(TopologyComponent.deviceLabelFlashMessage(next));
Sean Condonb2c483c2019-01-16 20:28:55 +0000527 this.updatePrefsState(PREF_DLBLS, next);
Sean Condon021f0fa2018-12-06 23:31:11 -0800528 this.log.debug('Cycling device labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100529 }
530
531 protected cycleHostLabels() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000532 const old: HostLabelToggle.Enum = this.prefsState.hlbls;
Sean Condon021f0fa2018-12-06 23:31:11 -0800533 const next = HostLabelToggle.next(old);
Sean Condon91481822019-01-01 13:56:14 +0000534 this.flashMsg = this.lionFn(TopologyComponent.hostLabelFlashMessage(next));
Sean Condonb2c483c2019-01-16 20:28:55 +0000535 this.updatePrefsState(PREF_HLBLS, next);
Sean Condon021f0fa2018-12-06 23:31:11 -0800536 this.log.debug('Cycling host labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100537 }
538
Sean Condon71910542019-02-16 18:16:42 +0000539 protected cycleGridDisplay() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000540 const old: GridDisplayToggle.Enum = this.prefsState.grid;
Sean Condon71910542019-02-16 18:16:42 +0000541 const next = GridDisplayToggle.next(old);
542 this.flashMsg = this.lionFn(TopologyComponent.gridDisplayFlashMessage(next));
543 this.updatePrefsState(PREF_GRID, next);
544 this.log.debug('Cycling grid display', old, next);
545 }
546
Sean Condon19e83672019-04-13 16:21:52 +0100547 protected cycleTrafficTypeDisplay() {
548 const old: TrafficType.Enum = this.prefsState.traffic; // by number
549 const next = TrafficType.next(old);
550 this.flashMsg = this.lionFn(TopologyComponent.trafficTypeFlashMessage(next));
551 this.updatePrefsState(PREF_TRAFFIC, next);
552 this.trs.requestTraffic(next);
553 this.log.debug('Cycling traffic display', old, next);
554 }
555
Sean Condonb2c483c2019-01-16 20:28:55 +0000556 /**
557 * When the button is clicked on the toolbar or the B key is pressed
558 * 1) Find the inverse of the current state (held as 1 or 0)
559 * 2) Flash up a message on screen
560 * 3b) And passes on to the global prefs service which sends back to the server
561 * 3c) It also has a knock on effect of passing it on to ToolbarComponent
562 * because prefsState.bg is given as an input to it
Sean Condonff85fbe2019-03-16 14:28:46 +0000563 * @param token not currently used
Sean Condonb2c483c2019-01-16 20:28:55 +0000564 */
Sean Condon91481822019-01-01 13:56:14 +0000565 protected toggleBackground(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000566 const bg: boolean = !Boolean(this.prefsState.bg);
567 this.flashMsg = this.lionFn(bg ? 'show' : 'hide') +
Sean Condon91481822019-01-01 13:56:14 +0000568 ' ' + this.lionFn('fl_background_map');
Sean Condonb2c483c2019-01-16 20:28:55 +0000569 this.updatePrefsState(PREF_BG, bg ? 1 : 0);
570 this.log.debug('Toggling background', token, bg ? 'shown' : 'hidden');
Sean Condonf4f54a12018-10-10 23:25:46 +0100571 }
572
Sean Condon91481822019-01-01 13:56:14 +0000573 protected toggleDetails(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000574 const on: boolean = !Boolean(this.prefsState.detail);
575 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
576 ' ' + this.lionFn('fl_panel_details');
577 this.updatePrefsState(PREF_DETAIL, on ? 1 : 0);
578 this.log.debug('Toggling details', token);
Sean Condonf4f54a12018-10-10 23:25:46 +0100579 }
580
Sean Condon91481822019-01-01 13:56:14 +0000581 protected toggleInstancePanel(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000582 const on: boolean = !Boolean(this.prefsState.insts);
Sean Condon91481822019-01-01 13:56:14 +0000583 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
584 ' ' + this.lionFn('fl_panel_instances');
Sean Condonb2c483c2019-01-16 20:28:55 +0000585 this.updatePrefsState(PREF_INSTS, on ? 1 : 0);
Sean Condon91481822019-01-01 13:56:14 +0000586 this.log.debug('Toggling instances', token, on);
Sean Condonf4f54a12018-10-10 23:25:46 +0100587 }
588
589 protected toggleSummary() {
Sean Condonb2c483c2019-01-16 20:28:55 +0000590 const on: boolean = !Boolean(this.prefsState.summary);
Sean Condon91481822019-01-01 13:56:14 +0000591 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
592 ' ' + this.lionFn('fl_panel_summary');
Sean Condonb2c483c2019-01-16 20:28:55 +0000593 this.updatePrefsState(PREF_SUMMARY, on ? 1 : 0);
594 }
595
596 protected togglePorts(token?: KeysToken) {
597 const current: boolean = !Boolean(this.prefsState.porthl);
598 this.flashMsg = this.lionFn(current ? 'enable' : 'disable') +
599 ' ' + this.lionFn('fl_port_highlighting');
600 this.updatePrefsState(PREF_PORTHL, current ? 1 : 0);
601 this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting');
602 }
603
604 protected toggleToolbar() {
605 const on: boolean = !Boolean(this.prefsState.toolbar);
606 this.updatePrefsState(PREF_TOOLBAR, on ? 1 : 0);
607 this.log.debug('toggling toolbar', on ? 'shown' : 'hidden');
608 }
609
610 protected toggleHosts() {
611 const current: boolean = !Boolean(this.prefsState.hosts);
612 this.flashMsg = this.lionFn('hosts') + ' ' +
613 this.lionFn(this.force.showHosts ? 'visible' : 'hidden');
614 this.updatePrefsState(PREF_HOSTS, current ? 1 : 0);
615 this.log.debug('toggling hosts: ', this.prefsState.hosts ? 'Show' : 'Hide');
616 }
617
618 protected toggleOfflineDevices() {
619 const on: boolean = !Boolean(this.prefsState.offdev);
620 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
621 ' ' + this.lionFn('fl_offline_devices');
622 this.updatePrefsState(PREF_OFFDEV, on ? 1 : 0);
623 this.log.debug('toggling offline devices', this.prefsState.offdev);
Sean Condonf4f54a12018-10-10 23:25:46 +0100624 }
625
626 protected resetZoom() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000627 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
628 this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
629 this.zoomDirective.changeZoomLevel(zoomMapExtents, false);
Sean Condon91481822019-01-01 13:56:14 +0000630 this.flashMsg = this.lionFn('fl_pan_zoom_reset');
Sean Condonf4f54a12018-10-10 23:25:46 +0100631 }
632
Sean Condonf4f54a12018-10-10 23:25:46 +0100633 protected equalizeMasters() {
Sean Condon64ea7d22019-04-12 19:39:13 +0100634 this.wss.sendEvent('equalizeMasters', {});
Sean Condon91481822019-01-01 13:56:14 +0000635 this.flashMsg = this.lionFn('fl_eq_masters');
Sean Condonf4f54a12018-10-10 23:25:46 +0100636 this.log.debug('equalizing masters');
Sean Condonf4f54a12018-10-10 23:25:46 +0100637 }
638
Sean Condon9de21352019-04-06 19:22:27 +0100639 /**
640 * If any nodes with fixed positions had been dragged out of place
641 * then put back where they belong
642 * If there are some devices selected reset only these
643 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100644 protected resetNodeLocation() {
Sean Condon9de21352019-04-06 19:22:27 +0100645 const numNodes = this.force.resetNodeLocations();
646 this.flashMsg = this.lionFn('fl_reset_node_locations') +
647 '(' + String(numNodes) + ')';
648 this.log.debug('resetting ', numNodes, 'node(s) location');
Sean Condonf4f54a12018-10-10 23:25:46 +0100649 }
650
Sean Condon9de21352019-04-06 19:22:27 +0100651 /**
652 * Toggle floating nodes between pinned and frozen
653 * If there are floating nodes selected toggle only these
654 */
655 protected unpinOrFreezeNodes() {
656 const pinned: boolean = !Boolean(this.prefsState.pinned);
657 const numNodes = this.force.unpinOrFreezeNodes(pinned);
658 this.flashMsg = this.lionFn(pinned ?
659 'fl_pinned_floating_nodes' : 'fl_unpinned_floating_nodes') +
660 '(' + String(numNodes) + ')';
661 this.updatePrefsState(PREF_PINNED, pinned ? 1 : 0);
662 this.log.debug('Toggling pinning for floating ', numNodes, 'nodes', pinned);
Sean Condonf4f54a12018-10-10 23:25:46 +0100663 }
664
Sean Condon91481822019-01-01 13:56:14 +0000665 /**
666 * Check to see if this is needed anymore
Sean Condonff85fbe2019-03-16 14:28:46 +0000667 * @param what - a key stroke
Sean Condon91481822019-01-01 13:56:14 +0000668 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100669 protected notValid(what) {
670 this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
671 }
672
Sean Condon91481822019-01-01 13:56:14 +0000673 /**
674 * Check to see if this is needed anymore
Sean Condonff85fbe2019-03-16 14:28:46 +0000675 * @param key - a key stroke
Sean Condon91481822019-01-01 13:56:14 +0000676 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100677 getActionEntry(key) {
678 let entry;
679
680 if (!key) {
681 this.notValid('key');
682 return null;
683 }
684
685 entry = this.actionMap()[key];
686
687 if (!entry) {
688 this.notValid('actionMap (' + key + ') entry');
689 return null;
690 }
691 return this.fs.isA(entry) || [entry, ''];
692 }
693
Sean Condon91481822019-01-01 13:56:14 +0000694 /**
695 * An event handler that updates the details panel as items are
696 * selected in the forcesvg layer
Sean Condond88f3662019-04-03 16:35:30 +0100697 *
698 * @param nodesOrLink the item(s) to display details of
Sean Condon91481822019-01-01 13:56:14 +0000699 */
Sean Condond88f3662019-04-03 16:35:30 +0100700 nodeSelected(nodesOrLink: UiElement[]) {
701 this.details.ngOnChanges({'selectedNodes':
702 new SimpleChange(undefined, nodesOrLink, true)});
Sean Condon50855cf2018-12-23 15:37:42 +0000703 }
704
705 /**
706 * Cancel traffic monitoring
707 */
708 cancelTraffic() {
Sean Condon91481822019-01-01 13:56:14 +0000709 this.flashMsg = this.lionFn('fl_monitoring_canceled');
Sean Condon19e83672019-04-13 16:21:52 +0100710 this.trs.cancelTraffic();
Sean Condon50855cf2018-12-23 15:37:42 +0000711 }
Sean Condon91481822019-01-01 13:56:14 +0000712
Sean Condon0d064ec2019-02-04 21:53:53 +0000713 changeMap(map: MapObject) {
714 this.mapSelShown = false; // Hide the MapSelector component
715 this.mapIdState = map;
716 this.ps.setPrefs(TOPO_MAPID_PREFS, this.mapIdState);
717 this.log.debug('Map has been changed to ', map);
718 }
719
Sean Condon1ae15802019-03-02 09:07:18 +0000720 mapExtentsZoom(zoomMapExtents: TopoZoomPrefs) {
721 // this.zoomDirective.updateZoomState(zoomPrefs.tx, zoomPrefs.ty, zoomPrefs.sc);
722 this.zoomDirective.changeZoomLevel(zoomMapExtents);
723 this.log.debug('Map zoom prefs updated', zoomMapExtents);
724 }
725
Sean Condon64ea7d22019-04-12 19:39:13 +0100726 backgroundClicked(event: MouseEvent) {
727 const elemTagName = event.target['tagName'] + ' ' + event.target['id'];
728 if (BACKGROUND_ELEMENTS.includes(elemTagName)) {
729 this.force.updateSelected(
730 <SelectedEvent>{
731 uiElement: undefined,
732 deselecting: true
733 }
734 );
735 }
736 }
737
Sean Condon91481822019-01-01 13:56:14 +0000738 /**
739 * Read the LION bundle for Toolbar and set up the lionFn
740 */
741 doLion() {
742 this.lionFn = this.lion.bundle('core.view.Topo');
743 }
744
745 /**
746 * A dummy implementation of the lionFn until the response is received and the LION
747 * bundle is received from the WebSocket
748 */
749 dummyLion(key: string): string {
750 return '%' + key + '%';
751 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100752}