blob: fb4e4cb2e2734e91951b1b1eb4811c3f306924fe [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 Condon4e55c802019-12-03 22:13:34 +000017 AfterViewInit,
Sean Condonff85fbe2019-03-16 14:28:46 +000018 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';
Sean Condon4e55c802019-12-03 22:13:34 +000041import {Intent, 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 Condon4e55c802019-12-03 22:13:34 +000073import {ActivatedRoute} from '@angular/router';
Sean Condonf4f54a12018-10-10 23:25:46 +010074
Sean Condonb2c483c2019-01-16 20:28:55 +000075const TOPO2_PREFS = 'topo2_prefs';
Sean Condon0d064ec2019-02-04 21:53:53 +000076const TOPO_MAPID_PREFS = 'topo_mapid';
77
Sean Condonb2c483c2019-01-16 20:28:55 +000078const PREF_BG = 'bg';
79const PREF_DETAIL = 'detail';
80const PREF_DLBLS = 'dlbls';
81const PREF_HLBLS = 'hlbls';
Sean Condon71910542019-02-16 18:16:42 +000082const PREF_GRID = 'grid';
Sean Condonb2c483c2019-01-16 20:28:55 +000083const PREF_HOSTS = 'hosts';
84const PREF_INSTS = 'insts';
85const PREF_OFFDEV = 'offdev';
86const PREF_PORTHL = 'porthl';
87const PREF_SUMMARY = 'summary';
88const PREF_TOOLBAR = 'toolbar';
Sean Condon9de21352019-04-06 19:22:27 +010089const PREF_PINNED = 'pinned';
Sean Condon19e83672019-04-13 16:21:52 +010090const PREF_TRAFFIC = 'traffic';
Sean Condonb2c483c2019-01-16 20:28:55 +000091
Sean Condon64ea7d22019-04-12 19:39:13 +010092const BACKGROUND_ELEMENTS = [
93 'svg topo2',
94 'path bgmap'
95];
96
Sean Condonb2c483c2019-01-16 20:28:55 +000097/**
Sean Condon0d064ec2019-02-04 21:53:53 +000098 * Model of the topo2_prefs object - this is a subset of the overall Prefs returned
Sean Condonb2c483c2019-01-16 20:28:55 +000099 * by the server
100 */
101export interface Topo2Prefs {
102 bg: number;
103 detail: number;
104 dlbls: number;
105 hlbls: number;
106 hosts: number;
107 insts: number;
108 offdev: number;
109 porthl: number;
110 spr: number;
111 ovid: string;
112 summary: number;
113 toolbar: number;
Sean Condon71910542019-02-16 18:16:42 +0000114 grid: number;
Sean Condon9de21352019-04-06 19:22:27 +0100115 pinned: number;
Sean Condon19e83672019-04-13 16:21:52 +0100116 traffic: number;
Sean Condonb2c483c2019-01-16 20:28:55 +0000117}
118
Sean Condonf4f54a12018-10-10 23:25:46 +0100119/**
120 * ONOS GUI Topology View
121 *
122 * This Topology View component is the top level component in a hierarchy that
123 * comprises the whole Topology View
124 *
125 * There are three main parts (panels, graphical and breadcrumbs)
126 * The panel hierarchy
127 * |-- Instances Panel (shows ONOS instances)
128 * |-- Summary Panel (summary of ONOS)
129 * |-- Toolbar Panel (the toolbar)
130 * |-- Details Panel (when a node is selected in the Force graphical view (see below))
131 *
132 * The graphical hierarchy contains
133 * Topology (this)
134 * |-- No Devices Connected (only of there are no nodes to show)
135 * |-- Zoom Layer (everything beneath this can be zoomed and panned)
136 * |-- Background (container for any backgrounds - can be toggled on and off)
137 * |-- Map
138 * |-- Forces (all of the nodes and links laid out by a d3.force simulation)
139 *
140 * The breadcrumbs
141 * |-- Breadcrumb (in region view a way of navigating back up through regions)
142 */
143@Component({
144 selector: 'onos-topology',
145 templateUrl: './topology.component.html',
146 styleUrls: ['./topology.component.css']
147})
Sean Condon4e55c802019-12-03 22:13:34 +0000148export class TopologyComponent implements OnInit, OnDestroy, AfterViewInit {
Sean Condonff85fbe2019-03-16 14:28:46 +0000149 @Input() bannerHeight: number = 48;
Sean Condonaa4366d2018-11-02 14:29:01 +0000150 // These are references to the components inserted in the template
Sean Condon0a884ad2019-10-28 17:57:21 +0000151 @ViewChild(InstanceComponent, {static: true}) instance: InstanceComponent;
152 @ViewChild(DetailsComponent, {static: true}) details: DetailsComponent;
153 @ViewChild(BackgroundSvgComponent, {static: true}) background: BackgroundSvgComponent;
154 @ViewChild(ForceSvgComponent, {static: true}) force: ForceSvgComponent;
155 @ViewChild(ZoomableDirective, {static: true}) zoomDirective: ZoomableDirective;
Sean Condonf4f54a12018-10-10 23:25:46 +0100156
157 flashMsg: string = '';
Sean Condonb2c483c2019-01-16 20:28:55 +0000158 // These are used as defaults if nothing is set on the server
159 prefsState: Topo2Prefs = <Topo2Prefs>{
160 bg: 0,
161 detail: 1,
162 dlbls: 0,
163 hlbls: 2,
164 hosts: 0,
165 insts: 1,
166 offdev: 1,
167 ovid: 'traffic', // default to traffic overlay
168 porthl: 1,
169 spr: 0,
170 summary: 1,
171 toolbar: 0,
Sean Condon19e83672019-04-13 16:21:52 +0100172 grid: 0,
173 pinned: 0,
174 traffic: 2 // default to PORTSTATSPKTSEC, as it will iterate over to 0 on init
Sean Condonb2c483c2019-01-16 20:28:55 +0000175 };
Sean Condon0d064ec2019-02-04 21:53:53 +0000176
177 mapIdState: MapObject = <MapObject>{
178 id: undefined,
179 scale: 1.0
180 };
181 mapSelShown: boolean = false;
Sean Condon91481822019-01-01 13:56:14 +0000182 lionFn; // Function
Sean Condon55c30532018-10-29 12:26:57 +0000183
Sean Condon71910542019-02-16 18:16:42 +0000184 gridShown: boolean = true;
185 geoGridShown: boolean = true;
186
Sean Condonf4f54a12018-10-10 23:25:46 +0100187 constructor(
188 protected log: LogService,
189 protected fs: FnService,
190 protected ks: KeysService,
191 protected sus: SvgUtilService,
192 protected ps: PrefsService,
Sean Condon55c30532018-10-29 12:26:57 +0000193 protected wss: WebSocketService,
Sean Condonaa4366d2018-11-02 14:29:01 +0000194 protected ts: TopologyService,
Sean Condon91481822019-01-01 13:56:14 +0000195 protected trs: TrafficService,
196 protected is: IconService,
197 private lion: LionService,
Sean Condon28884332019-03-21 14:07:00 +0000198 private layout: LayoutService,
Sean Condon4e55c802019-12-03 22:13:34 +0000199 protected ar: ActivatedRoute,
Sean Condonff85fbe2019-03-16 14:28:46 +0000200 @Inject('Window') public window: any,
Sean Condonf4f54a12018-10-10 23:25:46 +0100201 ) {
Sean Condon91481822019-01-01 13:56:14 +0000202 if (this.lion.ubercache.length === 0) {
203 this.lionFn = this.dummyLion;
204 this.lion.loadCbs.set('topo-toolbar', () => this.doLion());
205 } else {
206 this.doLion();
207 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100208
Sean Condon0a884ad2019-10-28 17:57:21 +0000209 this.log.warn('Constructor', this.zoomDirective);
210
Sean Condon91481822019-01-01 13:56:14 +0000211 this.is.loadIconDef('active');
Sean Condon4747ece2019-05-04 20:17:02 +0100212 this.is.loadIconDef('bgpSpeaker');
213 this.is.loadIconDef('bird');
214 this.is.loadIconDef('deviceTable');
215 this.is.loadIconDef('fiber_switch');
216 this.is.loadIconDef('flowTable');
217 this.is.loadIconDef('groupTable');
218 this.is.loadIconDef('m_allTraffic');
Sean Condon91481822019-01-01 13:56:14 +0000219 this.is.loadIconDef('m_cycleLabels');
Sean Condon71910542019-02-16 18:16:42 +0000220 this.is.loadIconDef('m_cycleGridDisplay');
Sean Condon28884332019-03-21 14:07:00 +0000221 this.is.loadIconDef('m_disjointPaths');
Sean Condon4747ece2019-05-04 20:17:02 +0100222 this.is.loadIconDef('m_details');
223 this.is.loadIconDef('m_endstation');
224 this.is.loadIconDef('m_eqMaster');
Sean Condon28884332019-03-21 14:07:00 +0000225 this.is.loadIconDef('m_fiberSwitch');
Sean Condon4747ece2019-05-04 20:17:02 +0100226 this.is.loadIconDef('m_firewall');
227 this.is.loadIconDef('m_map');
228 this.is.loadIconDef('m_microwave');
229 this.is.loadIconDef('m_ols');
230 this.is.loadIconDef('m_otn');
231 this.is.loadIconDef('m_ports');
232 this.is.loadIconDef('m_resetZoom');
233 this.is.loadIconDef('m_roadm');
234 this.is.loadIconDef('m_roadm_otn');
235 this.is.loadIconDef('m_router');
236 this.is.loadIconDef('m_selectMap');
237 this.is.loadIconDef('m_summary');
238 this.is.loadIconDef('m_switch');
239 this.is.loadIconDef('m_terminal_device');
240 this.is.loadIconDef('m_uiAttached');
241 this.is.loadIconDef('m_unknown');
242 this.is.loadIconDef('meterTable');
243 this.is.loadIconDef('microwave');
244 this.is.loadIconDef('otn');
245 this.is.loadIconDef('portTable');
246 this.is.loadIconDef('roadm_otn');
247 this.is.loadIconDef('triangleUp');
248 this.is.loadIconDef('uiAttached');
Sean Condonf4f54a12018-10-10 23:25:46 +0100249 }
250
Sean Condon91481822019-01-01 13:56:14 +0000251 /**
252 * Static functions must come before member variables
Sean Condonff85fbe2019-03-16 14:28:46 +0000253 * @param index Corresponds to LabelToggle.Enum index
Sean Condon91481822019-01-01 13:56:14 +0000254 */
Sean Condon021f0fa2018-12-06 23:31:11 -0800255 private static deviceLabelFlashMessage(index: number): string {
256 switch (index) {
Sean Condon91481822019-01-01 13:56:14 +0000257 case 0: return 'fl_device_labels_hide';
258 case 1: return 'fl_device_labels_show_friendly';
259 case 2: return 'fl_device_labels_show_id';
Sean Condon021f0fa2018-12-06 23:31:11 -0800260 }
261 }
262
263 private static hostLabelFlashMessage(index: number): string {
264 switch (index) {
Sean Condon91481822019-01-01 13:56:14 +0000265 case 0: return 'fl_host_labels_hide';
266 case 1: return 'fl_host_labels_show_friendly';
267 case 2: return 'fl_host_labels_show_ip';
268 case 3: return 'fl_host_labels_show_mac';
Sean Condon021f0fa2018-12-06 23:31:11 -0800269 }
270 }
271
Sean Condon71910542019-02-16 18:16:42 +0000272 private static gridDisplayFlashMessage(index: number): string {
273 switch (index) {
274 case 0: return 'fl_grid_display_hide';
275 case 1: return 'fl_grid_display_1000';
276 case 2: return 'fl_grid_display_geo';
277 case 3: return 'fl_grid_display_both';
278 }
279 }
280
Sean Condon19e83672019-04-13 16:21:52 +0100281 private static trafficTypeFlashMessage(index: number): string {
282 switch (index) {
283 case 0: return 'tr_fl_fstats_bytes';
284 case 1: return 'tr_fl_pstats_bits';
285 case 2: return 'tr_fl_pstats_pkts';
286 }
287 }
288
Sean Condon91481822019-01-01 13:56:14 +0000289 /**
290 * Pass the list of Key Commands to the KeyService, and initialize the Topology
291 * Service - which communicates with through the WebSocket to the ONOS server
292 * to get the nodes and links.
293 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100294 ngOnInit() {
295 this.bindCommands();
Sean Condon0a884ad2019-10-28 17:57:21 +0000296
Sean Condonaa4366d2018-11-02 14:29:01 +0000297 // The components from the template are handed over to TopologyService here
298 // so that WebSocket responses can be passed back in to them
299 // The handling of the WebSocket call is delegated out to the Topology
300 // Service just to compartmentalize things a bit
301 this.ts.init(this.instance, this.background, this.force);
Sean Condonb2c483c2019-01-16 20:28:55 +0000302
Sean Condonb0a196a2019-04-19 09:50:44 +0100303 // For the 2.1 release to not listen to updates of prefs as they are
304 // only the echo of what we have sent down and the event mechanism
305 // does not discern between users. Can get confused if multiple windows open
306 // this.ps.addListener((data) => this.prefsUpdateHandler(data));
307
Sean Condonb2c483c2019-01-16 20:28:55 +0000308 this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState);
Sean Condon0d064ec2019-02-04 21:53:53 +0000309 this.mapIdState = this.ps.getPrefs(TOPO_MAPID_PREFS, this.mapIdState);
Sean Condon19e83672019-04-13 16:21:52 +0100310 this.trs.init(this.force);
Sean Condon4e55c802019-12-03 22:13:34 +0000311
312 // Scale the window initially - then after resize
313 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
314 this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
315 this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
316
317 // TODO find out why the following is never printed
318 this.log.debug('TopologyComponent initialized,',
319 this.bannerHeight, this.window.innerWidth, this.window.innerHeight,
320 zoomMapExtents);
321 }
322
323 ngAfterViewInit(): void {
324 this.ar.queryParams.subscribe(params => {
325 const intentId = params['intentId'];
326 const intentType = params['intentType'];
327 const appId = params['appId'];
328 const appName = params['appName'];
329
330 if (intentId && intentType && appId) {
331 const selectedIntent = <Intent>{
332 key: intentId,
333 type: intentType,
334 appId: appId,
335 appName: appName,
336 };
337 this.ts.setSelectedIntent(selectedIntent);
338
339 this.log.warn('TopologyComponent init with Intent: ', selectedIntent, params);
340 }
341 });
Sean Condonff85fbe2019-03-16 14:28:46 +0000342 }
343
Sean Condon91481822019-01-01 13:56:14 +0000344 /**
Sean Condonb2c483c2019-01-16 20:28:55 +0000345 * Callback function that's called whenever new Prefs are received from WebSocket
346 *
347 * Note: At present the backend server does not filter updated by logged in user,
348 * so you might get updates pertaining to a different user
349 */
350 prefsUpdateHandler(data: any): void {
351 // Extract the TOPO2 prefs from it
Sean Condon0d064ec2019-02-04 21:53:53 +0000352 if (data[TOPO2_PREFS]) {
353 this.prefsState = data[TOPO2_PREFS];
354 }
Sean Condon0d064ec2019-02-04 21:53:53 +0000355 this.log.debug('Updated topo2 prefs', this.prefsState, this.mapIdState);
Sean Condonb2c483c2019-01-16 20:28:55 +0000356 }
357
358 /**
Sean Condon91481822019-01-01 13:56:14 +0000359 * When this component is being stopped, disconnect the TopologyService from
360 * the WebSocket
361 */
Sean Condonaa4366d2018-11-02 14:29:01 +0000362 ngOnDestroy() {
363 this.ts.destroy();
Sean Condonb2c483c2019-01-16 20:28:55 +0000364 this.ps.removeListener((data) => this.prefsUpdateHandler(data));
Sean Condon19e83672019-04-13 16:21:52 +0100365 this.trs.destroy();
Sean Condonaa4366d2018-11-02 14:29:01 +0000366 this.log.debug('Topology component destroyed');
367 }
368
Sean Condonff85fbe2019-03-16 14:28:46 +0000369 @HostListener('window:resize', ['$event'])
370 onResize(event) {
371 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
372 this.bannerHeight, event.target.innerWidth, event.target.innerHeight);
373 this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
374 this.log.debug('Topology window resize',
375 event.target.innerWidth, event.target.innerHeight, this.bannerHeight, zoomMapExtents);
376 }
377
Sean Condon91481822019-01-01 13:56:14 +0000378 /**
379 * When ever a toolbar button is clicked, an event is sent up from toolbar
380 * component which is caught and passed on to here.
381 * @param name The name of the button that was clicked
382 */
383 toolbarButtonClicked(name: string) {
384 switch (name) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000385 case INSTANCE_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000386 this.toggleInstancePanel();
387 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000388 case SUMMARY_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000389 this.toggleSummary();
390 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000391 case DETAILS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000392 this.toggleDetails();
393 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000394 case HOSTS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000395 this.toggleHosts();
396 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000397 case OFFLINE_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000398 this.toggleOfflineDevices();
399 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000400 case PORTS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000401 this.togglePorts();
402 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000403 case BKGRND_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000404 this.toggleBackground();
405 break;
Sean Condon0d064ec2019-02-04 21:53:53 +0000406 case BKGRND_SELECT:
407 this.mapSelShown = !this.mapSelShown;
408 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000409 case CYCLELABELS_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000410 this.cycleDeviceLabels();
411 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000412 case CYCLEHOSTLABEL_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000413 this.cycleHostLabels();
414 break;
Sean Condon71910542019-02-16 18:16:42 +0000415 case CYCLEGRIDDISPLAY_BTN:
416 this.cycleGridDisplay();
417 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000418 case RESETZOOM_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000419 this.resetZoom();
420 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000421 case EQMASTER_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000422 this.equalizeMasters();
423 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000424 case CANCEL_TRAFFIC:
Sean Condon91481822019-01-01 13:56:14 +0000425 this.cancelTraffic();
426 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000427 case ALL_TRAFFIC:
Sean Condon19e83672019-04-13 16:21:52 +0100428 this.cycleTrafficTypeDisplay();
Sean Condon91481822019-01-01 13:56:14 +0000429 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000430 case QUICKHELP_BTN:
431 this.ks.quickHelpShown = true;
432 break;
Sean Condon28884332019-03-21 14:07:00 +0000433 case LAYOUT_DEFAULT_BTN:
434 this.layout.changeLayout(LayoutType.LAYOUT_DEFAULT);
435 break;
436 case LAYOUT_ACCESS_BTN:
437 this.layout.changeLayout(LayoutType.LAYOUT_ACCESS);
438 break;
Sean Condon91481822019-01-01 13:56:14 +0000439 default:
440 this.log.warn('Unhandled Toolbar action', name);
441 }
442 }
443
444 /**
445 * The list of key strokes that will be active in the Topology View.
446 *
447 * This action map is passed to the KeyService through the bindCommands()
448 * when this component is being initialized
Sean Condonbed2e032019-04-17 22:22:49 +0100449 *
450 * TODO - Replace this doggy doo doo (copied over from GUI-1)
451 * with something more structured
Sean Condon91481822019-01-01 13:56:14 +0000452 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100453 actionMap() {
454 return {
Sean Condonbed2e032019-04-17 22:22:49 +0100455 A: [() => {this.cycleTrafficTypeDisplay(); }, this.lionFn('tr_btn_monitor_all')],
456 B: [(token) => {this.toggleBackground(token); }, this.lionFn('tbtt_tog_map')],
457 D: [(token) => {this.toggleDetails(token); }, this.lionFn('tbtt_tog_use_detail')],
458 E: [() => {this.equalizeMasters(); }, this.lionFn('tbtt_eq_master')],
459 H: [() => {this.toggleHosts(); }, this.lionFn('tbtt_tog_host')],
460 I: [(token) => {this.toggleInstancePanel(token); }, this.lionFn('tbtt_tog_instances')],
461 G: [() => {this.mapSelShown = !this.mapSelShown; }, this.lionFn('tbtt_sel_map')],
462 L: [() => {this.cycleDeviceLabels(); }, this.lionFn('tbtt_cyc_dev_labs')],
463 M: [() => {this.toggleOfflineDevices(); }, this.lionFn('tbtt_tog_offline')],
464 O: [() => {this.toggleSummary(); }, this.lionFn('tbtt_tog_summary')],
465 P: [(token) => {this.togglePorts(token); }, this.lionFn('tbtt_tog_porthi')],
466 Q: [() => {this.cycleGridDisplay(); }, this.lionFn('tbtt_cyc_grid_display')],
467 R: [() => {this.resetZoom(); }, this.lionFn('tbtt_reset_zoom')],
468 U: [() => {this.unpinOrFreezeNodes(); }, this.lionFn('tbtt_unpin_node')],
469 X: [() => {this.resetNodeLocation(); }, this.lionFn('tbtt_reset_loc')],
470 dot: [() => {this.toggleToolbar(); }, this.lionFn('tbtt_tog_toolbar')],
471 0: [() => {this.cancelTraffic(); }, this.lionFn('tr_btn_cancel_monitoring')],
472 'shift-L': [() => {this.cycleHostLabels(); }, this.lionFn('tbtt_cyc_host_labs')],
Sean Condonf4f54a12018-10-10 23:25:46 +0100473
474 // -- instance color palette debug
Sean Condon55c30532018-10-29 12:26:57 +0000475 9: () => {
476 this.sus.cat7().testCard(d3.select('svg#topo2'));
Sean Condonf4f54a12018-10-10 23:25:46 +0100477 },
478
Sean Condonbed2e032019-04-17 22:22:49 +0100479 esc: [() => {this.handleEscape(); }, this.lionFn('qh_hint_esc')],
Sean Condonf4f54a12018-10-10 23:25:46 +0100480
481 // TODO update after adding in Background Service
482 // topology overlay selections
483 // F1: function () { t2tbs.fnKey(0); },
484 // F2: function () { t2tbs.fnKey(1); },
485 // F3: function () { t2tbs.fnKey(2); },
486 // F4: function () { t2tbs.fnKey(3); },
487 // F5: function () { t2tbs.fnKey(4); },
488 //
489 // _keyListener: t2tbs.keyListener.bind(t2tbs),
490
491 _helpFormat: [
492 ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
493 ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
494 [], // this column reserved for overlay actions
495 ],
496 };
497 }
498
499
500 bindCommands(additional?: any) {
501
502 const am = this.actionMap();
Sean Condonf4f54a12018-10-10 23:25:46 +0100503 this.ks.keyBindings(am);
504
505 this.ks.gestureNotes([
506 ['click', 'Select the item and show details'],
507 ['shift-click', 'Toggle selection state'],
508 ['drag', 'Reposition (and pin) device / host'],
509 ['cmd-scroll', 'Zoom in / out'],
510 ['cmd-drag', 'Pan'],
511 ]);
512 }
513
514 handleEscape() {
515
516 if (false) {
517 // TODO: Cancel show mastership
518 // TODO: Cancel Active overlay
519 // TODO: Reinstate with components
520 } else {
Sean Condonb2c483c2019-01-16 20:28:55 +0000521 this.nodeSelected(undefined);
Sean Condonf4f54a12018-10-10 23:25:46 +0100522 this.log.debug('Handling escape');
523 // } else if (t2rs.deselectAllNodes()) {
524 // // else if we have node selections, deselect them all
525 // // (work already done)
526 // } else if (t2rs.deselectLink()) {
527 // // else if we have a link selection, deselect it
528 // // (work already done)
529 // } else if (t2is.isVisible()) {
530 // // If the instance panel is visible, close it
531 // t2is.toggle();
532 // } else if (t2sp.isVisible()) {
533 // // If the summary panel is visible, close it
534 // t2sp.toggle();
535 }
536 }
537
Sean Condonb2c483c2019-01-16 20:28:55 +0000538 /**
539 * Updates the cache of preferences locally and onwards to the PrefsService
540 * @param what The attribute of the local topo2-prefs cache to update
541 * @param b the value to update it with
542 */
543 updatePrefsState(what: string, b: number) {
544 this.prefsState[what] = b;
545 this.ps.setPrefs(TOPO2_PREFS, this.prefsState);
Sean Condonf4f54a12018-10-10 23:25:46 +0100546 }
547
Sean Condonb2c483c2019-01-16 20:28:55 +0000548 /**
549 * When the button is clicked on the toolbar or the L key is pressed
550 * 1) cycle through options
551 * 2) flash up a message
552 * 3a) Update the local prefs cache
553 * 3b) And passes on to the global prefs service which sends back to the server
554 * 3c) It also has a knock on effect of passing it on to ForceSvgComponent
555 * because prefsState.dlbls is given as an input to it
556 * 3d) This will in turn pass it down to the DeviceSvgComponent which
557 * displays the label
558 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100559 protected cycleDeviceLabels() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000560 const old: LabelToggle.Enum = this.prefsState.dlbls;
Sean Condon021f0fa2018-12-06 23:31:11 -0800561 const next = LabelToggle.next(old);
Sean Condon91481822019-01-01 13:56:14 +0000562 this.flashMsg = this.lionFn(TopologyComponent.deviceLabelFlashMessage(next));
Sean Condonb2c483c2019-01-16 20:28:55 +0000563 this.updatePrefsState(PREF_DLBLS, next);
Sean Condon021f0fa2018-12-06 23:31:11 -0800564 this.log.debug('Cycling device labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100565 }
566
567 protected cycleHostLabels() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000568 const old: HostLabelToggle.Enum = this.prefsState.hlbls;
Sean Condon021f0fa2018-12-06 23:31:11 -0800569 const next = HostLabelToggle.next(old);
Sean Condon91481822019-01-01 13:56:14 +0000570 this.flashMsg = this.lionFn(TopologyComponent.hostLabelFlashMessage(next));
Sean Condonb2c483c2019-01-16 20:28:55 +0000571 this.updatePrefsState(PREF_HLBLS, next);
Sean Condon021f0fa2018-12-06 23:31:11 -0800572 this.log.debug('Cycling host labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100573 }
574
Sean Condon71910542019-02-16 18:16:42 +0000575 protected cycleGridDisplay() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000576 const old: GridDisplayToggle.Enum = this.prefsState.grid;
Sean Condon71910542019-02-16 18:16:42 +0000577 const next = GridDisplayToggle.next(old);
578 this.flashMsg = this.lionFn(TopologyComponent.gridDisplayFlashMessage(next));
579 this.updatePrefsState(PREF_GRID, next);
580 this.log.debug('Cycling grid display', old, next);
581 }
582
Sean Condon19e83672019-04-13 16:21:52 +0100583 protected cycleTrafficTypeDisplay() {
584 const old: TrafficType.Enum = this.prefsState.traffic; // by number
585 const next = TrafficType.next(old);
586 this.flashMsg = this.lionFn(TopologyComponent.trafficTypeFlashMessage(next));
587 this.updatePrefsState(PREF_TRAFFIC, next);
588 this.trs.requestTraffic(next);
589 this.log.debug('Cycling traffic display', old, next);
590 }
591
Sean Condonb2c483c2019-01-16 20:28:55 +0000592 /**
593 * When the button is clicked on the toolbar or the B key is pressed
594 * 1) Find the inverse of the current state (held as 1 or 0)
595 * 2) Flash up a message on screen
596 * 3b) And passes on to the global prefs service which sends back to the server
597 * 3c) It also has a knock on effect of passing it on to ToolbarComponent
598 * because prefsState.bg is given as an input to it
Sean Condonff85fbe2019-03-16 14:28:46 +0000599 * @param token not currently used
Sean Condonb2c483c2019-01-16 20:28:55 +0000600 */
Sean Condon91481822019-01-01 13:56:14 +0000601 protected toggleBackground(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000602 const bg: boolean = !Boolean(this.prefsState.bg);
603 this.flashMsg = this.lionFn(bg ? 'show' : 'hide') +
Sean Condon91481822019-01-01 13:56:14 +0000604 ' ' + this.lionFn('fl_background_map');
Sean Condonb2c483c2019-01-16 20:28:55 +0000605 this.updatePrefsState(PREF_BG, bg ? 1 : 0);
606 this.log.debug('Toggling background', token, bg ? 'shown' : 'hidden');
Sean Condonf4f54a12018-10-10 23:25:46 +0100607 }
608
Sean Condon91481822019-01-01 13:56:14 +0000609 protected toggleDetails(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000610 const on: boolean = !Boolean(this.prefsState.detail);
611 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
612 ' ' + this.lionFn('fl_panel_details');
613 this.updatePrefsState(PREF_DETAIL, on ? 1 : 0);
614 this.log.debug('Toggling details', token);
Sean Condonf4f54a12018-10-10 23:25:46 +0100615 }
616
Sean Condon91481822019-01-01 13:56:14 +0000617 protected toggleInstancePanel(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000618 const on: boolean = !Boolean(this.prefsState.insts);
Sean Condon91481822019-01-01 13:56:14 +0000619 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
620 ' ' + this.lionFn('fl_panel_instances');
Sean Condonb2c483c2019-01-16 20:28:55 +0000621 this.updatePrefsState(PREF_INSTS, on ? 1 : 0);
Sean Condon91481822019-01-01 13:56:14 +0000622 this.log.debug('Toggling instances', token, on);
Sean Condonf4f54a12018-10-10 23:25:46 +0100623 }
624
625 protected toggleSummary() {
Sean Condonb2c483c2019-01-16 20:28:55 +0000626 const on: boolean = !Boolean(this.prefsState.summary);
Sean Condon91481822019-01-01 13:56:14 +0000627 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
628 ' ' + this.lionFn('fl_panel_summary');
Sean Condonb2c483c2019-01-16 20:28:55 +0000629 this.updatePrefsState(PREF_SUMMARY, on ? 1 : 0);
630 }
631
632 protected togglePorts(token?: KeysToken) {
633 const current: boolean = !Boolean(this.prefsState.porthl);
634 this.flashMsg = this.lionFn(current ? 'enable' : 'disable') +
635 ' ' + this.lionFn('fl_port_highlighting');
636 this.updatePrefsState(PREF_PORTHL, current ? 1 : 0);
637 this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting');
638 }
639
640 protected toggleToolbar() {
641 const on: boolean = !Boolean(this.prefsState.toolbar);
642 this.updatePrefsState(PREF_TOOLBAR, on ? 1 : 0);
643 this.log.debug('toggling toolbar', on ? 'shown' : 'hidden');
644 }
645
646 protected toggleHosts() {
647 const current: boolean = !Boolean(this.prefsState.hosts);
648 this.flashMsg = this.lionFn('hosts') + ' ' +
Sean Condonb0a196a2019-04-19 09:50:44 +0100649 this.lionFn(current ? 'visible' : 'hidden');
Sean Condonb2c483c2019-01-16 20:28:55 +0000650 this.updatePrefsState(PREF_HOSTS, current ? 1 : 0);
651 this.log.debug('toggling hosts: ', this.prefsState.hosts ? 'Show' : 'Hide');
652 }
653
654 protected toggleOfflineDevices() {
655 const on: boolean = !Boolean(this.prefsState.offdev);
656 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
657 ' ' + this.lionFn('fl_offline_devices');
658 this.updatePrefsState(PREF_OFFDEV, on ? 1 : 0);
659 this.log.debug('toggling offline devices', this.prefsState.offdev);
Sean Condonf4f54a12018-10-10 23:25:46 +0100660 }
661
662 protected resetZoom() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000663 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
664 this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
665 this.zoomDirective.changeZoomLevel(zoomMapExtents, false);
Sean Condon91481822019-01-01 13:56:14 +0000666 this.flashMsg = this.lionFn('fl_pan_zoom_reset');
Sean Condonf4f54a12018-10-10 23:25:46 +0100667 }
668
Sean Condonf4f54a12018-10-10 23:25:46 +0100669 protected equalizeMasters() {
Sean Condon64ea7d22019-04-12 19:39:13 +0100670 this.wss.sendEvent('equalizeMasters', {});
Sean Condon91481822019-01-01 13:56:14 +0000671 this.flashMsg = this.lionFn('fl_eq_masters');
Sean Condonf4f54a12018-10-10 23:25:46 +0100672 this.log.debug('equalizing masters');
Sean Condonf4f54a12018-10-10 23:25:46 +0100673 }
674
Sean Condon9de21352019-04-06 19:22:27 +0100675 /**
676 * If any nodes with fixed positions had been dragged out of place
677 * then put back where they belong
678 * If there are some devices selected reset only these
679 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100680 protected resetNodeLocation() {
Sean Condon9de21352019-04-06 19:22:27 +0100681 const numNodes = this.force.resetNodeLocations();
682 this.flashMsg = this.lionFn('fl_reset_node_locations') +
683 '(' + String(numNodes) + ')';
684 this.log.debug('resetting ', numNodes, 'node(s) location');
Sean Condonf4f54a12018-10-10 23:25:46 +0100685 }
686
Sean Condon9de21352019-04-06 19:22:27 +0100687 /**
688 * Toggle floating nodes between pinned and frozen
689 * If there are floating nodes selected toggle only these
690 */
691 protected unpinOrFreezeNodes() {
692 const pinned: boolean = !Boolean(this.prefsState.pinned);
693 const numNodes = this.force.unpinOrFreezeNodes(pinned);
694 this.flashMsg = this.lionFn(pinned ?
695 'fl_pinned_floating_nodes' : 'fl_unpinned_floating_nodes') +
696 '(' + String(numNodes) + ')';
697 this.updatePrefsState(PREF_PINNED, pinned ? 1 : 0);
698 this.log.debug('Toggling pinning for floating ', numNodes, 'nodes', pinned);
Sean Condonf4f54a12018-10-10 23:25:46 +0100699 }
700
Sean Condon91481822019-01-01 13:56:14 +0000701 /**
702 * Check to see if this is needed anymore
Sean Condonff85fbe2019-03-16 14:28:46 +0000703 * @param what - a key stroke
Sean Condon91481822019-01-01 13:56:14 +0000704 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100705 protected notValid(what) {
706 this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
707 }
708
Sean Condon91481822019-01-01 13:56:14 +0000709 /**
710 * Check to see if this is needed anymore
Sean Condonff85fbe2019-03-16 14:28:46 +0000711 * @param key - a key stroke
Sean Condon91481822019-01-01 13:56:14 +0000712 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100713 getActionEntry(key) {
714 let entry;
715
716 if (!key) {
717 this.notValid('key');
718 return null;
719 }
720
721 entry = this.actionMap()[key];
722
723 if (!entry) {
724 this.notValid('actionMap (' + key + ') entry');
725 return null;
726 }
727 return this.fs.isA(entry) || [entry, ''];
728 }
729
Sean Condon91481822019-01-01 13:56:14 +0000730 /**
731 * An event handler that updates the details panel as items are
732 * selected in the forcesvg layer
Sean Condond88f3662019-04-03 16:35:30 +0100733 *
734 * @param nodesOrLink the item(s) to display details of
Sean Condon91481822019-01-01 13:56:14 +0000735 */
Sean Condond88f3662019-04-03 16:35:30 +0100736 nodeSelected(nodesOrLink: UiElement[]) {
737 this.details.ngOnChanges({'selectedNodes':
738 new SimpleChange(undefined, nodesOrLink, true)});
Sean Condon50855cf2018-12-23 15:37:42 +0000739 }
740
741 /**
742 * Cancel traffic monitoring
743 */
744 cancelTraffic() {
Sean Condon91481822019-01-01 13:56:14 +0000745 this.flashMsg = this.lionFn('fl_monitoring_canceled');
Sean Condon19e83672019-04-13 16:21:52 +0100746 this.trs.cancelTraffic();
Sean Condon50855cf2018-12-23 15:37:42 +0000747 }
Sean Condon91481822019-01-01 13:56:14 +0000748
Sean Condon0d064ec2019-02-04 21:53:53 +0000749 changeMap(map: MapObject) {
750 this.mapSelShown = false; // Hide the MapSelector component
751 this.mapIdState = map;
752 this.ps.setPrefs(TOPO_MAPID_PREFS, this.mapIdState);
753 this.log.debug('Map has been changed to ', map);
754 }
755
Sean Condon1ae15802019-03-02 09:07:18 +0000756 mapExtentsZoom(zoomMapExtents: TopoZoomPrefs) {
757 // this.zoomDirective.updateZoomState(zoomPrefs.tx, zoomPrefs.ty, zoomPrefs.sc);
758 this.zoomDirective.changeZoomLevel(zoomMapExtents);
759 this.log.debug('Map zoom prefs updated', zoomMapExtents);
760 }
761
Sean Condon64ea7d22019-04-12 19:39:13 +0100762 backgroundClicked(event: MouseEvent) {
763 const elemTagName = event.target['tagName'] + ' ' + event.target['id'];
764 if (BACKGROUND_ELEMENTS.includes(elemTagName)) {
765 this.force.updateSelected(
766 <SelectedEvent>{
767 uiElement: undefined,
768 deselecting: true
769 }
770 );
771 }
Sean Condon4e55c802019-12-03 22:13:34 +0000772 this.ts.cancelHighlights();
773 this.force.cancelAllLinkHighlightsNow();
Sean Condon64ea7d22019-04-12 19:39:13 +0100774 }
775
Sean Condon91481822019-01-01 13:56:14 +0000776 /**
777 * Read the LION bundle for Toolbar and set up the lionFn
778 */
779 doLion() {
780 this.lionFn = this.lion.bundle('core.view.Topo');
781 }
782
783 /**
784 * A dummy implementation of the lionFn until the response is received and the LION
785 * bundle is received from the WebSocket
786 */
787 dummyLion(key: string): string {
788 return '%' + key + '%';
789 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100790}