blob: 10f961b278f2a0ad8080344d06d0f0ca145c5c27 [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 Condon3dd062f2020-04-14 09:25:00 +010036} from 'org_onosproject_onos/web/gui2-fw-lib/public_api';
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,
Sean Condon590b34b2019-12-04 18:44:37 +000066 SUMMARY_TOGGLE,
67 ALARMS_TOGGLE
Sean Condonb2c483c2019-01-16 20:28:55 +000068} from '../panel/toolbar/toolbar.component';
Sean Condon19e83672019-04-13 16:21:52 +010069import {TrafficService, TrafficType} from '../traffic.service';
Sean Condon3dd062f2020-04-14 09:25:00 +010070import {ZoomableDirective} from 'org_onosproject_onos/web/gui2-fw-lib/public_api';
Sean Condon0d064ec2019-02-04 21:53:53 +000071import {MapObject} from '../layer/maputils';
Sean Condon28884332019-03-21 14:07:00 +000072import {LayoutService, LayoutType} from '../layout.service';
Sean Condon64ea7d22019-04-12 19:39:13 +010073import {SelectedEvent} from '../layer/forcesvg/visuals/nodevisual';
Sean Condon4e55c802019-12-03 22:13:34 +000074import {ActivatedRoute} from '@angular/router';
Sean Condonf4f54a12018-10-10 23:25:46 +010075
Sean Condonb2c483c2019-01-16 20:28:55 +000076const TOPO2_PREFS = 'topo2_prefs';
Sean Condon0d064ec2019-02-04 21:53:53 +000077const TOPO_MAPID_PREFS = 'topo_mapid';
78
Sean Condonb2c483c2019-01-16 20:28:55 +000079const PREF_BG = 'bg';
80const PREF_DETAIL = 'detail';
81const PREF_DLBLS = 'dlbls';
82const PREF_HLBLS = 'hlbls';
Sean Condon71910542019-02-16 18:16:42 +000083const PREF_GRID = 'grid';
Sean Condonb2c483c2019-01-16 20:28:55 +000084const PREF_HOSTS = 'hosts';
85const PREF_INSTS = 'insts';
86const PREF_OFFDEV = 'offdev';
87const PREF_PORTHL = 'porthl';
88const PREF_SUMMARY = 'summary';
89const PREF_TOOLBAR = 'toolbar';
Sean Condon9de21352019-04-06 19:22:27 +010090const PREF_PINNED = 'pinned';
Sean Condon19e83672019-04-13 16:21:52 +010091const PREF_TRAFFIC = 'traffic';
Sean Condon590b34b2019-12-04 18:44:37 +000092const PREF_ALARMS = 'alarms';
Sean Condonb2c483c2019-01-16 20:28:55 +000093
Sean Condon64ea7d22019-04-12 19:39:13 +010094const BACKGROUND_ELEMENTS = [
95 'svg topo2',
96 'path bgmap'
97];
98
Sean Condonb2c483c2019-01-16 20:28:55 +000099/**
Sean Condon0d064ec2019-02-04 21:53:53 +0000100 * Model of the topo2_prefs object - this is a subset of the overall Prefs returned
Sean Condonb2c483c2019-01-16 20:28:55 +0000101 * by the server
102 */
103export interface Topo2Prefs {
104 bg: number;
105 detail: number;
106 dlbls: number;
107 hlbls: number;
108 hosts: number;
109 insts: number;
110 offdev: number;
111 porthl: number;
112 spr: number;
113 ovid: string;
114 summary: number;
115 toolbar: number;
Sean Condon71910542019-02-16 18:16:42 +0000116 grid: number;
Sean Condon9de21352019-04-06 19:22:27 +0100117 pinned: number;
Sean Condon19e83672019-04-13 16:21:52 +0100118 traffic: number;
Sean Condon590b34b2019-12-04 18:44:37 +0000119 alarms: number;
Sean Condonb2c483c2019-01-16 20:28:55 +0000120}
121
Sean Condonf4f54a12018-10-10 23:25:46 +0100122/**
123 * ONOS GUI Topology View
124 *
125 * This Topology View component is the top level component in a hierarchy that
126 * comprises the whole Topology View
127 *
128 * There are three main parts (panels, graphical and breadcrumbs)
129 * The panel hierarchy
130 * |-- Instances Panel (shows ONOS instances)
131 * |-- Summary Panel (summary of ONOS)
132 * |-- Toolbar Panel (the toolbar)
133 * |-- Details Panel (when a node is selected in the Force graphical view (see below))
134 *
135 * The graphical hierarchy contains
136 * Topology (this)
137 * |-- No Devices Connected (only of there are no nodes to show)
138 * |-- Zoom Layer (everything beneath this can be zoomed and panned)
139 * |-- Background (container for any backgrounds - can be toggled on and off)
140 * |-- Map
141 * |-- Forces (all of the nodes and links laid out by a d3.force simulation)
142 *
143 * The breadcrumbs
144 * |-- Breadcrumb (in region view a way of navigating back up through regions)
145 */
146@Component({
147 selector: 'onos-topology',
148 templateUrl: './topology.component.html',
149 styleUrls: ['./topology.component.css']
150})
Sean Condon4e55c802019-12-03 22:13:34 +0000151export class TopologyComponent implements OnInit, OnDestroy, AfterViewInit {
Sean Condonff85fbe2019-03-16 14:28:46 +0000152 @Input() bannerHeight: number = 48;
Sean Condonaa4366d2018-11-02 14:29:01 +0000153 // These are references to the components inserted in the template
Sean Condon0a884ad2019-10-28 17:57:21 +0000154 @ViewChild(InstanceComponent, {static: true}) instance: InstanceComponent;
155 @ViewChild(DetailsComponent, {static: true}) details: DetailsComponent;
156 @ViewChild(BackgroundSvgComponent, {static: true}) background: BackgroundSvgComponent;
157 @ViewChild(ForceSvgComponent, {static: true}) force: ForceSvgComponent;
158 @ViewChild(ZoomableDirective, {static: true}) zoomDirective: ZoomableDirective;
Sean Condonf4f54a12018-10-10 23:25:46 +0100159
160 flashMsg: string = '';
Sean Condonb2c483c2019-01-16 20:28:55 +0000161 // These are used as defaults if nothing is set on the server
162 prefsState: Topo2Prefs = <Topo2Prefs>{
163 bg: 0,
164 detail: 1,
165 dlbls: 0,
166 hlbls: 2,
167 hosts: 0,
168 insts: 1,
169 offdev: 1,
170 ovid: 'traffic', // default to traffic overlay
171 porthl: 1,
172 spr: 0,
173 summary: 1,
174 toolbar: 0,
Sean Condon19e83672019-04-13 16:21:52 +0100175 grid: 0,
176 pinned: 0,
Sean Condon590b34b2019-12-04 18:44:37 +0000177 traffic: 2, // default to PORTSTATSPKTSEC, as it will iterate over to 0 on init
178 alarms: 1,
Sean Condonb2c483c2019-01-16 20:28:55 +0000179 };
Sean Condon0d064ec2019-02-04 21:53:53 +0000180
181 mapIdState: MapObject = <MapObject>{
182 id: undefined,
183 scale: 1.0
184 };
185 mapSelShown: boolean = false;
Sean Condon91481822019-01-01 13:56:14 +0000186 lionFn; // Function
Sean Condon55c30532018-10-29 12:26:57 +0000187
Sean Condon71910542019-02-16 18:16:42 +0000188 gridShown: boolean = true;
189 geoGridShown: boolean = true;
190
Sean Condonf4f54a12018-10-10 23:25:46 +0100191 constructor(
192 protected log: LogService,
193 protected fs: FnService,
194 protected ks: KeysService,
195 protected sus: SvgUtilService,
196 protected ps: PrefsService,
Sean Condon55c30532018-10-29 12:26:57 +0000197 protected wss: WebSocketService,
Sean Condonaa4366d2018-11-02 14:29:01 +0000198 protected ts: TopologyService,
Sean Condon91481822019-01-01 13:56:14 +0000199 protected trs: TrafficService,
200 protected is: IconService,
201 private lion: LionService,
Sean Condon28884332019-03-21 14:07:00 +0000202 private layout: LayoutService,
Sean Condon4e55c802019-12-03 22:13:34 +0000203 protected ar: ActivatedRoute,
Sean Condonff85fbe2019-03-16 14:28:46 +0000204 @Inject('Window') public window: any,
Sean Condonf4f54a12018-10-10 23:25:46 +0100205 ) {
Sean Condon91481822019-01-01 13:56:14 +0000206 if (this.lion.ubercache.length === 0) {
207 this.lionFn = this.dummyLion;
208 this.lion.loadCbs.set('topo-toolbar', () => this.doLion());
209 } else {
210 this.doLion();
211 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100212
Sean Condon0a884ad2019-10-28 17:57:21 +0000213 this.log.warn('Constructor', this.zoomDirective);
214
Sean Condon91481822019-01-01 13:56:14 +0000215 this.is.loadIconDef('active');
Sean Condon4747ece2019-05-04 20:17:02 +0100216 this.is.loadIconDef('bgpSpeaker');
217 this.is.loadIconDef('bird');
218 this.is.loadIconDef('deviceTable');
219 this.is.loadIconDef('fiber_switch');
220 this.is.loadIconDef('flowTable');
221 this.is.loadIconDef('groupTable');
222 this.is.loadIconDef('m_allTraffic');
Sean Condon91481822019-01-01 13:56:14 +0000223 this.is.loadIconDef('m_cycleLabels');
Sean Condon71910542019-02-16 18:16:42 +0000224 this.is.loadIconDef('m_cycleGridDisplay');
Sean Condon28884332019-03-21 14:07:00 +0000225 this.is.loadIconDef('m_disjointPaths');
Sean Condon4747ece2019-05-04 20:17:02 +0100226 this.is.loadIconDef('m_details');
227 this.is.loadIconDef('m_endstation');
228 this.is.loadIconDef('m_eqMaster');
Sean Condon28884332019-03-21 14:07:00 +0000229 this.is.loadIconDef('m_fiberSwitch');
Sean Condon4747ece2019-05-04 20:17:02 +0100230 this.is.loadIconDef('m_firewall');
231 this.is.loadIconDef('m_map');
232 this.is.loadIconDef('m_microwave');
233 this.is.loadIconDef('m_ols');
234 this.is.loadIconDef('m_otn');
235 this.is.loadIconDef('m_ports');
236 this.is.loadIconDef('m_resetZoom');
237 this.is.loadIconDef('m_roadm');
238 this.is.loadIconDef('m_roadm_otn');
239 this.is.loadIconDef('m_router');
240 this.is.loadIconDef('m_selectMap');
241 this.is.loadIconDef('m_summary');
242 this.is.loadIconDef('m_switch');
243 this.is.loadIconDef('m_terminal_device');
244 this.is.loadIconDef('m_uiAttached');
245 this.is.loadIconDef('m_unknown');
246 this.is.loadIconDef('meterTable');
247 this.is.loadIconDef('microwave');
248 this.is.loadIconDef('otn');
249 this.is.loadIconDef('portTable');
250 this.is.loadIconDef('roadm_otn');
251 this.is.loadIconDef('triangleUp');
252 this.is.loadIconDef('uiAttached');
Sean Condonf4f54a12018-10-10 23:25:46 +0100253 }
254
Sean Condon91481822019-01-01 13:56:14 +0000255 /**
256 * Static functions must come before member variables
Sean Condonff85fbe2019-03-16 14:28:46 +0000257 * @param index Corresponds to LabelToggle.Enum index
Sean Condon91481822019-01-01 13:56:14 +0000258 */
Sean Condon021f0fa2018-12-06 23:31:11 -0800259 private static deviceLabelFlashMessage(index: number): string {
260 switch (index) {
Sean Condon91481822019-01-01 13:56:14 +0000261 case 0: return 'fl_device_labels_hide';
262 case 1: return 'fl_device_labels_show_friendly';
263 case 2: return 'fl_device_labels_show_id';
Sean Condon021f0fa2018-12-06 23:31:11 -0800264 }
265 }
266
267 private static hostLabelFlashMessage(index: number): string {
268 switch (index) {
Sean Condon91481822019-01-01 13:56:14 +0000269 case 0: return 'fl_host_labels_hide';
270 case 1: return 'fl_host_labels_show_friendly';
271 case 2: return 'fl_host_labels_show_ip';
272 case 3: return 'fl_host_labels_show_mac';
Sean Condon021f0fa2018-12-06 23:31:11 -0800273 }
274 }
275
Sean Condon71910542019-02-16 18:16:42 +0000276 private static gridDisplayFlashMessage(index: number): string {
277 switch (index) {
278 case 0: return 'fl_grid_display_hide';
279 case 1: return 'fl_grid_display_1000';
280 case 2: return 'fl_grid_display_geo';
281 case 3: return 'fl_grid_display_both';
282 }
283 }
284
Sean Condon19e83672019-04-13 16:21:52 +0100285 private static trafficTypeFlashMessage(index: number): string {
286 switch (index) {
287 case 0: return 'tr_fl_fstats_bytes';
288 case 1: return 'tr_fl_pstats_bits';
289 case 2: return 'tr_fl_pstats_pkts';
290 }
291 }
292
Sean Condon91481822019-01-01 13:56:14 +0000293 /**
294 * Pass the list of Key Commands to the KeyService, and initialize the Topology
295 * Service - which communicates with through the WebSocket to the ONOS server
296 * to get the nodes and links.
297 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100298 ngOnInit() {
299 this.bindCommands();
Sean Condon0a884ad2019-10-28 17:57:21 +0000300
Sean Condonaa4366d2018-11-02 14:29:01 +0000301 // The components from the template are handed over to TopologyService here
302 // so that WebSocket responses can be passed back in to them
303 // The handling of the WebSocket call is delegated out to the Topology
304 // Service just to compartmentalize things a bit
305 this.ts.init(this.instance, this.background, this.force);
Sean Condonb2c483c2019-01-16 20:28:55 +0000306
Sean Condonb0a196a2019-04-19 09:50:44 +0100307 // For the 2.1 release to not listen to updates of prefs as they are
308 // only the echo of what we have sent down and the event mechanism
309 // does not discern between users. Can get confused if multiple windows open
310 // this.ps.addListener((data) => this.prefsUpdateHandler(data));
311
Sean Condonb2c483c2019-01-16 20:28:55 +0000312 this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState);
Sean Condon0d064ec2019-02-04 21:53:53 +0000313 this.mapIdState = this.ps.getPrefs(TOPO_MAPID_PREFS, this.mapIdState);
Sean Condon19e83672019-04-13 16:21:52 +0100314 this.trs.init(this.force);
Sean Condon4e55c802019-12-03 22:13:34 +0000315
316 // Scale the window initially - then after resize
317 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
318 this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
319 this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
320
321 // TODO find out why the following is never printed
322 this.log.debug('TopologyComponent initialized,',
323 this.bannerHeight, this.window.innerWidth, this.window.innerHeight,
324 zoomMapExtents);
325 }
326
327 ngAfterViewInit(): void {
328 this.ar.queryParams.subscribe(params => {
329 const intentId = params['intentId'];
330 const intentType = params['intentType'];
331 const appId = params['appId'];
332 const appName = params['appName'];
333
334 if (intentId && intentType && appId) {
335 const selectedIntent = <Intent>{
336 key: intentId,
337 type: intentType,
338 appId: appId,
339 appName: appName,
340 };
341 this.ts.setSelectedIntent(selectedIntent);
342
343 this.log.warn('TopologyComponent init with Intent: ', selectedIntent, params);
344 }
345 });
Sean Condonff85fbe2019-03-16 14:28:46 +0000346 }
347
Sean Condon91481822019-01-01 13:56:14 +0000348 /**
Sean Condonb2c483c2019-01-16 20:28:55 +0000349 * Callback function that's called whenever new Prefs are received from WebSocket
350 *
351 * Note: At present the backend server does not filter updated by logged in user,
352 * so you might get updates pertaining to a different user
353 */
354 prefsUpdateHandler(data: any): void {
355 // Extract the TOPO2 prefs from it
Sean Condon0d064ec2019-02-04 21:53:53 +0000356 if (data[TOPO2_PREFS]) {
357 this.prefsState = data[TOPO2_PREFS];
358 }
Sean Condon0d064ec2019-02-04 21:53:53 +0000359 this.log.debug('Updated topo2 prefs', this.prefsState, this.mapIdState);
Sean Condonb2c483c2019-01-16 20:28:55 +0000360 }
361
362 /**
Sean Condon91481822019-01-01 13:56:14 +0000363 * When this component is being stopped, disconnect the TopologyService from
364 * the WebSocket
365 */
Sean Condonaa4366d2018-11-02 14:29:01 +0000366 ngOnDestroy() {
367 this.ts.destroy();
Sean Condonb2c483c2019-01-16 20:28:55 +0000368 this.ps.removeListener((data) => this.prefsUpdateHandler(data));
Sean Condon19e83672019-04-13 16:21:52 +0100369 this.trs.destroy();
Sean Condonaa4366d2018-11-02 14:29:01 +0000370 this.log.debug('Topology component destroyed');
371 }
372
Sean Condonff85fbe2019-03-16 14:28:46 +0000373 @HostListener('window:resize', ['$event'])
374 onResize(event) {
375 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
376 this.bannerHeight, event.target.innerWidth, event.target.innerHeight);
377 this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
378 this.log.debug('Topology window resize',
379 event.target.innerWidth, event.target.innerHeight, this.bannerHeight, zoomMapExtents);
380 }
381
Sean Condon91481822019-01-01 13:56:14 +0000382 /**
383 * When ever a toolbar button is clicked, an event is sent up from toolbar
384 * component which is caught and passed on to here.
385 * @param name The name of the button that was clicked
386 */
387 toolbarButtonClicked(name: string) {
388 switch (name) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000389 case INSTANCE_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000390 this.toggleInstancePanel();
391 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000392 case SUMMARY_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000393 this.toggleSummary();
394 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000395 case DETAILS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000396 this.toggleDetails();
397 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000398 case HOSTS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000399 this.toggleHosts();
400 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000401 case OFFLINE_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000402 this.toggleOfflineDevices();
403 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000404 case PORTS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000405 this.togglePorts();
406 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000407 case BKGRND_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000408 this.toggleBackground();
409 break;
Sean Condon0d064ec2019-02-04 21:53:53 +0000410 case BKGRND_SELECT:
411 this.mapSelShown = !this.mapSelShown;
412 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000413 case CYCLELABELS_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000414 this.cycleDeviceLabels();
415 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000416 case CYCLEHOSTLABEL_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000417 this.cycleHostLabels();
418 break;
Sean Condon71910542019-02-16 18:16:42 +0000419 case CYCLEGRIDDISPLAY_BTN:
420 this.cycleGridDisplay();
421 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000422 case RESETZOOM_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000423 this.resetZoom();
424 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000425 case EQMASTER_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000426 this.equalizeMasters();
427 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000428 case CANCEL_TRAFFIC:
Sean Condon91481822019-01-01 13:56:14 +0000429 this.cancelTraffic();
430 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000431 case ALL_TRAFFIC:
Sean Condon19e83672019-04-13 16:21:52 +0100432 this.cycleTrafficTypeDisplay();
Sean Condon91481822019-01-01 13:56:14 +0000433 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000434 case QUICKHELP_BTN:
435 this.ks.quickHelpShown = true;
436 break;
Sean Condon28884332019-03-21 14:07:00 +0000437 case LAYOUT_DEFAULT_BTN:
438 this.layout.changeLayout(LayoutType.LAYOUT_DEFAULT);
439 break;
440 case LAYOUT_ACCESS_BTN:
441 this.layout.changeLayout(LayoutType.LAYOUT_ACCESS);
442 break;
Sean Condon590b34b2019-12-04 18:44:37 +0000443 case ALARMS_TOGGLE:
444 this.toggleAlarms();
445 break;
Sean Condon91481822019-01-01 13:56:14 +0000446 default:
447 this.log.warn('Unhandled Toolbar action', name);
448 }
449 }
450
451 /**
452 * The list of key strokes that will be active in the Topology View.
453 *
454 * This action map is passed to the KeyService through the bindCommands()
455 * when this component is being initialized
Sean Condonbed2e032019-04-17 22:22:49 +0100456 *
457 * TODO - Replace this doggy doo doo (copied over from GUI-1)
458 * with something more structured
Sean Condon91481822019-01-01 13:56:14 +0000459 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100460 actionMap() {
461 return {
Sean Condonbed2e032019-04-17 22:22:49 +0100462 A: [() => {this.cycleTrafficTypeDisplay(); }, this.lionFn('tr_btn_monitor_all')],
463 B: [(token) => {this.toggleBackground(token); }, this.lionFn('tbtt_tog_map')],
464 D: [(token) => {this.toggleDetails(token); }, this.lionFn('tbtt_tog_use_detail')],
465 E: [() => {this.equalizeMasters(); }, this.lionFn('tbtt_eq_master')],
466 H: [() => {this.toggleHosts(); }, this.lionFn('tbtt_tog_host')],
467 I: [(token) => {this.toggleInstancePanel(token); }, this.lionFn('tbtt_tog_instances')],
468 G: [() => {this.mapSelShown = !this.mapSelShown; }, this.lionFn('tbtt_sel_map')],
469 L: [() => {this.cycleDeviceLabels(); }, this.lionFn('tbtt_cyc_dev_labs')],
470 M: [() => {this.toggleOfflineDevices(); }, this.lionFn('tbtt_tog_offline')],
471 O: [() => {this.toggleSummary(); }, this.lionFn('tbtt_tog_summary')],
472 P: [(token) => {this.togglePorts(token); }, this.lionFn('tbtt_tog_porthi')],
473 Q: [() => {this.cycleGridDisplay(); }, this.lionFn('tbtt_cyc_grid_display')],
474 R: [() => {this.resetZoom(); }, this.lionFn('tbtt_reset_zoom')],
475 U: [() => {this.unpinOrFreezeNodes(); }, this.lionFn('tbtt_unpin_node')],
476 X: [() => {this.resetNodeLocation(); }, this.lionFn('tbtt_reset_loc')],
477 dot: [() => {this.toggleToolbar(); }, this.lionFn('tbtt_tog_toolbar')],
478 0: [() => {this.cancelTraffic(); }, this.lionFn('tr_btn_cancel_monitoring')],
479 'shift-L': [() => {this.cycleHostLabels(); }, this.lionFn('tbtt_cyc_host_labs')],
Sean Condonf4f54a12018-10-10 23:25:46 +0100480
481 // -- instance color palette debug
Sean Condon55c30532018-10-29 12:26:57 +0000482 9: () => {
483 this.sus.cat7().testCard(d3.select('svg#topo2'));
Sean Condonf4f54a12018-10-10 23:25:46 +0100484 },
485
Sean Condonbed2e032019-04-17 22:22:49 +0100486 esc: [() => {this.handleEscape(); }, this.lionFn('qh_hint_esc')],
Sean Condonf4f54a12018-10-10 23:25:46 +0100487
488 // TODO update after adding in Background Service
489 // topology overlay selections
490 // F1: function () { t2tbs.fnKey(0); },
491 // F2: function () { t2tbs.fnKey(1); },
492 // F3: function () { t2tbs.fnKey(2); },
493 // F4: function () { t2tbs.fnKey(3); },
494 // F5: function () { t2tbs.fnKey(4); },
495 //
496 // _keyListener: t2tbs.keyListener.bind(t2tbs),
497
498 _helpFormat: [
499 ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
500 ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
501 [], // this column reserved for overlay actions
502 ],
503 };
504 }
505
506
507 bindCommands(additional?: any) {
508
509 const am = this.actionMap();
Sean Condonf4f54a12018-10-10 23:25:46 +0100510 this.ks.keyBindings(am);
511
512 this.ks.gestureNotes([
513 ['click', 'Select the item and show details'],
514 ['shift-click', 'Toggle selection state'],
515 ['drag', 'Reposition (and pin) device / host'],
516 ['cmd-scroll', 'Zoom in / out'],
517 ['cmd-drag', 'Pan'],
518 ]);
519 }
520
521 handleEscape() {
522
523 if (false) {
524 // TODO: Cancel show mastership
525 // TODO: Cancel Active overlay
526 // TODO: Reinstate with components
527 } else {
Sean Condonb2c483c2019-01-16 20:28:55 +0000528 this.nodeSelected(undefined);
Sean Condonf4f54a12018-10-10 23:25:46 +0100529 this.log.debug('Handling escape');
530 // } else if (t2rs.deselectAllNodes()) {
531 // // else if we have node selections, deselect them all
532 // // (work already done)
533 // } else if (t2rs.deselectLink()) {
534 // // else if we have a link selection, deselect it
535 // // (work already done)
536 // } else if (t2is.isVisible()) {
537 // // If the instance panel is visible, close it
538 // t2is.toggle();
539 // } else if (t2sp.isVisible()) {
540 // // If the summary panel is visible, close it
541 // t2sp.toggle();
542 }
543 }
544
Sean Condonb2c483c2019-01-16 20:28:55 +0000545 /**
546 * Updates the cache of preferences locally and onwards to the PrefsService
547 * @param what The attribute of the local topo2-prefs cache to update
548 * @param b the value to update it with
549 */
550 updatePrefsState(what: string, b: number) {
551 this.prefsState[what] = b;
552 this.ps.setPrefs(TOPO2_PREFS, this.prefsState);
Sean Condonf4f54a12018-10-10 23:25:46 +0100553 }
554
Sean Condonb2c483c2019-01-16 20:28:55 +0000555 /**
556 * When the button is clicked on the toolbar or the L key is pressed
557 * 1) cycle through options
558 * 2) flash up a message
559 * 3a) Update the local prefs cache
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 ForceSvgComponent
562 * because prefsState.dlbls is given as an input to it
563 * 3d) This will in turn pass it down to the DeviceSvgComponent which
564 * displays the label
565 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100566 protected cycleDeviceLabels() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000567 const old: LabelToggle.Enum = this.prefsState.dlbls;
Sean Condon021f0fa2018-12-06 23:31:11 -0800568 const next = LabelToggle.next(old);
Sean Condon91481822019-01-01 13:56:14 +0000569 this.flashMsg = this.lionFn(TopologyComponent.deviceLabelFlashMessage(next));
Sean Condonb2c483c2019-01-16 20:28:55 +0000570 this.updatePrefsState(PREF_DLBLS, next);
Sean Condon021f0fa2018-12-06 23:31:11 -0800571 this.log.debug('Cycling device labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100572 }
573
574 protected cycleHostLabels() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000575 const old: HostLabelToggle.Enum = this.prefsState.hlbls;
Sean Condon021f0fa2018-12-06 23:31:11 -0800576 const next = HostLabelToggle.next(old);
Sean Condon91481822019-01-01 13:56:14 +0000577 this.flashMsg = this.lionFn(TopologyComponent.hostLabelFlashMessage(next));
Sean Condonb2c483c2019-01-16 20:28:55 +0000578 this.updatePrefsState(PREF_HLBLS, next);
Sean Condon021f0fa2018-12-06 23:31:11 -0800579 this.log.debug('Cycling host labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100580 }
581
Sean Condon71910542019-02-16 18:16:42 +0000582 protected cycleGridDisplay() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000583 const old: GridDisplayToggle.Enum = this.prefsState.grid;
Sean Condon71910542019-02-16 18:16:42 +0000584 const next = GridDisplayToggle.next(old);
585 this.flashMsg = this.lionFn(TopologyComponent.gridDisplayFlashMessage(next));
586 this.updatePrefsState(PREF_GRID, next);
587 this.log.debug('Cycling grid display', old, next);
588 }
589
Sean Condon19e83672019-04-13 16:21:52 +0100590 protected cycleTrafficTypeDisplay() {
591 const old: TrafficType.Enum = this.prefsState.traffic; // by number
592 const next = TrafficType.next(old);
593 this.flashMsg = this.lionFn(TopologyComponent.trafficTypeFlashMessage(next));
594 this.updatePrefsState(PREF_TRAFFIC, next);
595 this.trs.requestTraffic(next);
596 this.log.debug('Cycling traffic display', old, next);
597 }
598
Sean Condonb2c483c2019-01-16 20:28:55 +0000599 /**
600 * When the button is clicked on the toolbar or the B key is pressed
601 * 1) Find the inverse of the current state (held as 1 or 0)
602 * 2) Flash up a message on screen
603 * 3b) And passes on to the global prefs service which sends back to the server
604 * 3c) It also has a knock on effect of passing it on to ToolbarComponent
605 * because prefsState.bg is given as an input to it
Sean Condonff85fbe2019-03-16 14:28:46 +0000606 * @param token not currently used
Sean Condonb2c483c2019-01-16 20:28:55 +0000607 */
Sean Condon91481822019-01-01 13:56:14 +0000608 protected toggleBackground(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000609 const bg: boolean = !Boolean(this.prefsState.bg);
610 this.flashMsg = this.lionFn(bg ? 'show' : 'hide') +
Sean Condon91481822019-01-01 13:56:14 +0000611 ' ' + this.lionFn('fl_background_map');
Sean Condonb2c483c2019-01-16 20:28:55 +0000612 this.updatePrefsState(PREF_BG, bg ? 1 : 0);
613 this.log.debug('Toggling background', token, bg ? 'shown' : 'hidden');
Sean Condonf4f54a12018-10-10 23:25:46 +0100614 }
615
Sean Condon91481822019-01-01 13:56:14 +0000616 protected toggleDetails(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000617 const on: boolean = !Boolean(this.prefsState.detail);
618 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
619 ' ' + this.lionFn('fl_panel_details');
620 this.updatePrefsState(PREF_DETAIL, on ? 1 : 0);
621 this.log.debug('Toggling details', token);
Sean Condonf4f54a12018-10-10 23:25:46 +0100622 }
623
Sean Condon91481822019-01-01 13:56:14 +0000624 protected toggleInstancePanel(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000625 const on: boolean = !Boolean(this.prefsState.insts);
Sean Condon91481822019-01-01 13:56:14 +0000626 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
627 ' ' + this.lionFn('fl_panel_instances');
Sean Condonb2c483c2019-01-16 20:28:55 +0000628 this.updatePrefsState(PREF_INSTS, on ? 1 : 0);
Sean Condon91481822019-01-01 13:56:14 +0000629 this.log.debug('Toggling instances', token, on);
Sean Condonf4f54a12018-10-10 23:25:46 +0100630 }
631
632 protected toggleSummary() {
Sean Condonb2c483c2019-01-16 20:28:55 +0000633 const on: boolean = !Boolean(this.prefsState.summary);
Sean Condon91481822019-01-01 13:56:14 +0000634 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
635 ' ' + this.lionFn('fl_panel_summary');
Sean Condonb2c483c2019-01-16 20:28:55 +0000636 this.updatePrefsState(PREF_SUMMARY, on ? 1 : 0);
637 }
638
639 protected togglePorts(token?: KeysToken) {
640 const current: boolean = !Boolean(this.prefsState.porthl);
641 this.flashMsg = this.lionFn(current ? 'enable' : 'disable') +
642 ' ' + this.lionFn('fl_port_highlighting');
643 this.updatePrefsState(PREF_PORTHL, current ? 1 : 0);
644 this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting');
645 }
646
647 protected toggleToolbar() {
648 const on: boolean = !Boolean(this.prefsState.toolbar);
649 this.updatePrefsState(PREF_TOOLBAR, on ? 1 : 0);
650 this.log.debug('toggling toolbar', on ? 'shown' : 'hidden');
651 }
652
653 protected toggleHosts() {
654 const current: boolean = !Boolean(this.prefsState.hosts);
655 this.flashMsg = this.lionFn('hosts') + ' ' +
Sean Condonb0a196a2019-04-19 09:50:44 +0100656 this.lionFn(current ? 'visible' : 'hidden');
Sean Condonb2c483c2019-01-16 20:28:55 +0000657 this.updatePrefsState(PREF_HOSTS, current ? 1 : 0);
658 this.log.debug('toggling hosts: ', this.prefsState.hosts ? 'Show' : 'Hide');
659 }
660
661 protected toggleOfflineDevices() {
662 const on: boolean = !Boolean(this.prefsState.offdev);
663 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
664 ' ' + this.lionFn('fl_offline_devices');
665 this.updatePrefsState(PREF_OFFDEV, on ? 1 : 0);
666 this.log.debug('toggling offline devices', this.prefsState.offdev);
Sean Condonf4f54a12018-10-10 23:25:46 +0100667 }
668
Sean Condon590b34b2019-12-04 18:44:37 +0000669
670 protected toggleAlarms() {
671 const on: boolean = !Boolean(this.prefsState.alarms);
672 this.flashMsg = this.lionFn(on ? 'show' : 'hide') + ' Alarms';
673 this.updatePrefsState(PREF_ALARMS, on ? 1 : 0);
674 this.log.debug('Alarms toggled', on);
675 }
676
Sean Condonf4f54a12018-10-10 23:25:46 +0100677 protected resetZoom() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000678 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
679 this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
680 this.zoomDirective.changeZoomLevel(zoomMapExtents, false);
Sean Condon91481822019-01-01 13:56:14 +0000681 this.flashMsg = this.lionFn('fl_pan_zoom_reset');
Sean Condonf4f54a12018-10-10 23:25:46 +0100682 }
683
Sean Condonf4f54a12018-10-10 23:25:46 +0100684 protected equalizeMasters() {
Sean Condon64ea7d22019-04-12 19:39:13 +0100685 this.wss.sendEvent('equalizeMasters', {});
Sean Condon91481822019-01-01 13:56:14 +0000686 this.flashMsg = this.lionFn('fl_eq_masters');
Sean Condonf4f54a12018-10-10 23:25:46 +0100687 this.log.debug('equalizing masters');
Sean Condonf4f54a12018-10-10 23:25:46 +0100688 }
689
Sean Condon9de21352019-04-06 19:22:27 +0100690 /**
691 * If any nodes with fixed positions had been dragged out of place
692 * then put back where they belong
693 * If there are some devices selected reset only these
694 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100695 protected resetNodeLocation() {
Sean Condon9de21352019-04-06 19:22:27 +0100696 const numNodes = this.force.resetNodeLocations();
697 this.flashMsg = this.lionFn('fl_reset_node_locations') +
698 '(' + String(numNodes) + ')';
699 this.log.debug('resetting ', numNodes, 'node(s) location');
Sean Condonf4f54a12018-10-10 23:25:46 +0100700 }
701
Sean Condon9de21352019-04-06 19:22:27 +0100702 /**
703 * Toggle floating nodes between pinned and frozen
704 * If there are floating nodes selected toggle only these
705 */
706 protected unpinOrFreezeNodes() {
707 const pinned: boolean = !Boolean(this.prefsState.pinned);
708 const numNodes = this.force.unpinOrFreezeNodes(pinned);
709 this.flashMsg = this.lionFn(pinned ?
710 'fl_pinned_floating_nodes' : 'fl_unpinned_floating_nodes') +
711 '(' + String(numNodes) + ')';
712 this.updatePrefsState(PREF_PINNED, pinned ? 1 : 0);
713 this.log.debug('Toggling pinning for floating ', numNodes, 'nodes', pinned);
Sean Condonf4f54a12018-10-10 23:25:46 +0100714 }
715
Sean Condon91481822019-01-01 13:56:14 +0000716 /**
717 * Check to see if this is needed anymore
Sean Condonff85fbe2019-03-16 14:28:46 +0000718 * @param what - a key stroke
Sean Condon91481822019-01-01 13:56:14 +0000719 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100720 protected notValid(what) {
721 this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
722 }
723
Sean Condon91481822019-01-01 13:56:14 +0000724 /**
725 * Check to see if this is needed anymore
Sean Condonff85fbe2019-03-16 14:28:46 +0000726 * @param key - a key stroke
Sean Condon91481822019-01-01 13:56:14 +0000727 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100728 getActionEntry(key) {
729 let entry;
730
731 if (!key) {
732 this.notValid('key');
733 return null;
734 }
735
736 entry = this.actionMap()[key];
737
738 if (!entry) {
739 this.notValid('actionMap (' + key + ') entry');
740 return null;
741 }
742 return this.fs.isA(entry) || [entry, ''];
743 }
744
Sean Condon91481822019-01-01 13:56:14 +0000745 /**
746 * An event handler that updates the details panel as items are
747 * selected in the forcesvg layer
Sean Condond88f3662019-04-03 16:35:30 +0100748 *
749 * @param nodesOrLink the item(s) to display details of
Sean Condon91481822019-01-01 13:56:14 +0000750 */
Sean Condond88f3662019-04-03 16:35:30 +0100751 nodeSelected(nodesOrLink: UiElement[]) {
752 this.details.ngOnChanges({'selectedNodes':
753 new SimpleChange(undefined, nodesOrLink, true)});
Sean Condon50855cf2018-12-23 15:37:42 +0000754 }
755
756 /**
757 * Cancel traffic monitoring
758 */
759 cancelTraffic() {
Sean Condon91481822019-01-01 13:56:14 +0000760 this.flashMsg = this.lionFn('fl_monitoring_canceled');
Sean Condon19e83672019-04-13 16:21:52 +0100761 this.trs.cancelTraffic();
Sean Condon50855cf2018-12-23 15:37:42 +0000762 }
Sean Condon91481822019-01-01 13:56:14 +0000763
Sean Condon0d064ec2019-02-04 21:53:53 +0000764 changeMap(map: MapObject) {
765 this.mapSelShown = false; // Hide the MapSelector component
766 this.mapIdState = map;
767 this.ps.setPrefs(TOPO_MAPID_PREFS, this.mapIdState);
768 this.log.debug('Map has been changed to ', map);
769 }
770
Sean Condon1ae15802019-03-02 09:07:18 +0000771 mapExtentsZoom(zoomMapExtents: TopoZoomPrefs) {
772 // this.zoomDirective.updateZoomState(zoomPrefs.tx, zoomPrefs.ty, zoomPrefs.sc);
773 this.zoomDirective.changeZoomLevel(zoomMapExtents);
774 this.log.debug('Map zoom prefs updated', zoomMapExtents);
775 }
776
Sean Condon64ea7d22019-04-12 19:39:13 +0100777 backgroundClicked(event: MouseEvent) {
778 const elemTagName = event.target['tagName'] + ' ' + event.target['id'];
779 if (BACKGROUND_ELEMENTS.includes(elemTagName)) {
780 this.force.updateSelected(
781 <SelectedEvent>{
782 uiElement: undefined,
783 deselecting: true
784 }
785 );
786 }
Sean Condon4e55c802019-12-03 22:13:34 +0000787 this.ts.cancelHighlights();
788 this.force.cancelAllLinkHighlightsNow();
Sean Condon64ea7d22019-04-12 19:39:13 +0100789 }
790
Sean Condon91481822019-01-01 13:56:14 +0000791 /**
792 * Read the LION bundle for Toolbar and set up the lionFn
793 */
794 doLion() {
795 this.lionFn = this.lion.bundle('core.view.Topo');
796 }
797
798 /**
799 * A dummy implementation of the lionFn until the response is received and the LION
800 * bundle is received from the WebSocket
801 */
802 dummyLion(key: string): string {
803 return '%' + key + '%';
804 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100805}