blob: 0aeaee7377bd39d1fb7ca6aedb619a7a46c41c29 [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 Component, HostListener, Inject, Input,
Sean Condon0c577f62018-11-18 22:40:05 +000018 OnDestroy,
Sean Condon021f0fa2018-12-06 23:31:11 -080019 OnInit, SimpleChange,
Sean Condon0c577f62018-11-18 22:40:05 +000020 ViewChild
21} from '@angular/core';
Sean Condon55c30532018-10-29 12:26:57 +000022import * as d3 from 'd3';
Sean Condonf4f54a12018-10-10 23:25:46 +010023import {
Sean Condon28884332019-03-21 14:07:00 +000024 FnService,
25 IconService,
Sean Condon0c577f62018-11-18 22:40:05 +000026 KeysService,
Sean Condon28884332019-03-21 14:07:00 +000027 KeysToken,
28 LionService,
Sean Condon0c577f62018-11-18 22:40:05 +000029 LogService,
30 PrefsService,
31 SvgUtilService,
Sean Condon28884332019-03-21 14:07:00 +000032 TopoZoomPrefs,
Sean Condon0c577f62018-11-18 22:40:05 +000033 WebSocketService,
Sean Condon28884332019-03-21 14:07:00 +000034 ZoomUtils
Sean Condonf4f54a12018-10-10 23:25:46 +010035} from 'gui2-fw-lib';
Sean Condon0c577f62018-11-18 22:40:05 +000036import {InstanceComponent} from '../panel/instance/instance.component';
Sean Condon0c577f62018-11-18 22:40:05 +000037import {DetailsComponent} from '../panel/details/details.component';
38import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component';
39import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
40import {TopologyService} from '../topology.service';
Sean Condon91481822019-01-01 13:56:14 +000041import {
Sean Condon71910542019-02-16 18:16:42 +000042 GridDisplayToggle,
Sean Condon91481822019-01-01 13:56:14 +000043 HostLabelToggle,
44 LabelToggle,
45 UiElement
46} from '../layer/forcesvg/models';
Sean Condonb2c483c2019-01-16 20:28:55 +000047import {
Sean Condon28884332019-03-21 14:07:00 +000048 ALL_TRAFFIC,
49 BKGRND_SELECT,
50 BKGRND_TOGGLE,
51 CANCEL_TRAFFIC,
52 CYCLEGRIDDISPLAY_BTN,
53 CYCLEHOSTLABEL_BTN,
54 CYCLELABELS_BTN,
55 DETAILS_TOGGLE,
56 EQMASTER_BTN,
57 HOSTS_TOGGLE,
58 INSTANCE_TOGGLE,
59 LAYOUT_ACCESS_BTN,
60 LAYOUT_DEFAULT_BTN,
61 OFFLINE_TOGGLE,
62 PORTS_TOGGLE,
63 QUICKHELP_BTN,
64 RESETZOOM_BTN,
65 SUMMARY_TOGGLE
Sean Condonb2c483c2019-01-16 20:28:55 +000066} from '../panel/toolbar/toolbar.component';
Sean Condon19e83672019-04-13 16:21:52 +010067import {TrafficService, TrafficType} from '../traffic.service';
Sean Condon91481822019-01-01 13:56:14 +000068import {ZoomableDirective} from '../layer/zoomable.directive';
Sean Condon0d064ec2019-02-04 21:53:53 +000069import {MapObject} from '../layer/maputils';
Sean Condon28884332019-03-21 14:07:00 +000070import {LayoutService, LayoutType} from '../layout.service';
Sean Condon64ea7d22019-04-12 19:39:13 +010071import {SelectedEvent} from '../layer/forcesvg/visuals/nodevisual';
Sean Condonf4f54a12018-10-10 23:25:46 +010072
Sean Condonb2c483c2019-01-16 20:28:55 +000073const TOPO2_PREFS = 'topo2_prefs';
Sean Condon0d064ec2019-02-04 21:53:53 +000074const TOPO_MAPID_PREFS = 'topo_mapid';
75
Sean Condonb2c483c2019-01-16 20:28:55 +000076const PREF_BG = 'bg';
77const PREF_DETAIL = 'detail';
78const PREF_DLBLS = 'dlbls';
79const PREF_HLBLS = 'hlbls';
Sean Condon71910542019-02-16 18:16:42 +000080const PREF_GRID = 'grid';
Sean Condonb2c483c2019-01-16 20:28:55 +000081const PREF_HOSTS = 'hosts';
82const PREF_INSTS = 'insts';
83const PREF_OFFDEV = 'offdev';
84const PREF_PORTHL = 'porthl';
85const PREF_SUMMARY = 'summary';
86const PREF_TOOLBAR = 'toolbar';
Sean Condon9de21352019-04-06 19:22:27 +010087const PREF_PINNED = 'pinned';
Sean Condon19e83672019-04-13 16:21:52 +010088const PREF_TRAFFIC = 'traffic';
Sean Condonb2c483c2019-01-16 20:28:55 +000089
Sean Condon64ea7d22019-04-12 19:39:13 +010090const BACKGROUND_ELEMENTS = [
91 'svg topo2',
92 'path bgmap'
93];
94
Sean Condonb2c483c2019-01-16 20:28:55 +000095/**
Sean Condon0d064ec2019-02-04 21:53:53 +000096 * Model of the topo2_prefs object - this is a subset of the overall Prefs returned
Sean Condonb2c483c2019-01-16 20:28:55 +000097 * by the server
98 */
99export interface Topo2Prefs {
100 bg: number;
101 detail: number;
102 dlbls: number;
103 hlbls: number;
104 hosts: number;
105 insts: number;
106 offdev: number;
107 porthl: number;
108 spr: number;
109 ovid: string;
110 summary: number;
111 toolbar: number;
Sean Condon71910542019-02-16 18:16:42 +0000112 grid: number;
Sean Condon9de21352019-04-06 19:22:27 +0100113 pinned: number;
Sean Condon19e83672019-04-13 16:21:52 +0100114 traffic: number;
Sean Condonb2c483c2019-01-16 20:28:55 +0000115}
116
Sean Condonf4f54a12018-10-10 23:25:46 +0100117/**
118 * ONOS GUI Topology View
119 *
120 * This Topology View component is the top level component in a hierarchy that
121 * comprises the whole Topology View
122 *
123 * There are three main parts (panels, graphical and breadcrumbs)
124 * The panel hierarchy
125 * |-- Instances Panel (shows ONOS instances)
126 * |-- Summary Panel (summary of ONOS)
127 * |-- Toolbar Panel (the toolbar)
128 * |-- Details Panel (when a node is selected in the Force graphical view (see below))
129 *
130 * The graphical hierarchy contains
131 * Topology (this)
132 * |-- No Devices Connected (only of there are no nodes to show)
133 * |-- Zoom Layer (everything beneath this can be zoomed and panned)
134 * |-- Background (container for any backgrounds - can be toggled on and off)
135 * |-- Map
136 * |-- Forces (all of the nodes and links laid out by a d3.force simulation)
137 *
138 * The breadcrumbs
139 * |-- Breadcrumb (in region view a way of navigating back up through regions)
140 */
141@Component({
142 selector: 'onos-topology',
143 templateUrl: './topology.component.html',
144 styleUrls: ['./topology.component.css']
145})
Sean Condonb0a196a2019-04-19 09:50:44 +0100146export class TopologyComponent implements OnInit, OnDestroy {
Sean Condonff85fbe2019-03-16 14:28:46 +0000147 @Input() bannerHeight: number = 48;
Sean Condonaa4366d2018-11-02 14:29:01 +0000148 // These are references to the components inserted in the template
Sean Condon0a884ad2019-10-28 17:57:21 +0000149 @ViewChild(InstanceComponent, {static: true}) instance: InstanceComponent;
150 @ViewChild(DetailsComponent, {static: true}) details: DetailsComponent;
151 @ViewChild(BackgroundSvgComponent, {static: true}) background: BackgroundSvgComponent;
152 @ViewChild(ForceSvgComponent, {static: true}) force: ForceSvgComponent;
153 @ViewChild(ZoomableDirective, {static: true}) zoomDirective: ZoomableDirective;
Sean Condonf4f54a12018-10-10 23:25:46 +0100154
155 flashMsg: string = '';
Sean Condonb2c483c2019-01-16 20:28:55 +0000156 // These are used as defaults if nothing is set on the server
157 prefsState: Topo2Prefs = <Topo2Prefs>{
158 bg: 0,
159 detail: 1,
160 dlbls: 0,
161 hlbls: 2,
162 hosts: 0,
163 insts: 1,
164 offdev: 1,
165 ovid: 'traffic', // default to traffic overlay
166 porthl: 1,
167 spr: 0,
168 summary: 1,
169 toolbar: 0,
Sean Condon19e83672019-04-13 16:21:52 +0100170 grid: 0,
171 pinned: 0,
172 traffic: 2 // default to PORTSTATSPKTSEC, as it will iterate over to 0 on init
Sean Condonb2c483c2019-01-16 20:28:55 +0000173 };
Sean Condon0d064ec2019-02-04 21:53:53 +0000174
175 mapIdState: MapObject = <MapObject>{
176 id: undefined,
177 scale: 1.0
178 };
179 mapSelShown: boolean = false;
Sean Condon91481822019-01-01 13:56:14 +0000180 lionFn; // Function
Sean Condon55c30532018-10-29 12:26:57 +0000181
Sean Condon71910542019-02-16 18:16:42 +0000182 gridShown: boolean = true;
183 geoGridShown: boolean = true;
184
Sean Condonf4f54a12018-10-10 23:25:46 +0100185 constructor(
186 protected log: LogService,
187 protected fs: FnService,
188 protected ks: KeysService,
189 protected sus: SvgUtilService,
190 protected ps: PrefsService,
Sean Condon55c30532018-10-29 12:26:57 +0000191 protected wss: WebSocketService,
Sean Condonaa4366d2018-11-02 14:29:01 +0000192 protected ts: TopologyService,
Sean Condon91481822019-01-01 13:56:14 +0000193 protected trs: TrafficService,
194 protected is: IconService,
195 private lion: LionService,
Sean Condon28884332019-03-21 14:07:00 +0000196 private layout: LayoutService,
Sean Condonff85fbe2019-03-16 14:28:46 +0000197 @Inject('Window') public window: any,
Sean Condonf4f54a12018-10-10 23:25:46 +0100198 ) {
Sean Condon91481822019-01-01 13:56:14 +0000199 if (this.lion.ubercache.length === 0) {
200 this.lionFn = this.dummyLion;
201 this.lion.loadCbs.set('topo-toolbar', () => this.doLion());
202 } else {
203 this.doLion();
204 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100205
Sean Condon0a884ad2019-10-28 17:57:21 +0000206 this.log.warn('Constructor', this.zoomDirective);
207
Sean Condon91481822019-01-01 13:56:14 +0000208 this.is.loadIconDef('active');
Sean Condon4747ece2019-05-04 20:17:02 +0100209 this.is.loadIconDef('bgpSpeaker');
210 this.is.loadIconDef('bird');
211 this.is.loadIconDef('deviceTable');
212 this.is.loadIconDef('fiber_switch');
213 this.is.loadIconDef('flowTable');
214 this.is.loadIconDef('groupTable');
215 this.is.loadIconDef('m_allTraffic');
Sean Condon91481822019-01-01 13:56:14 +0000216 this.is.loadIconDef('m_cycleLabels');
Sean Condon71910542019-02-16 18:16:42 +0000217 this.is.loadIconDef('m_cycleGridDisplay');
Sean Condon28884332019-03-21 14:07:00 +0000218 this.is.loadIconDef('m_disjointPaths');
Sean Condon4747ece2019-05-04 20:17:02 +0100219 this.is.loadIconDef('m_details');
220 this.is.loadIconDef('m_endstation');
221 this.is.loadIconDef('m_eqMaster');
Sean Condon28884332019-03-21 14:07:00 +0000222 this.is.loadIconDef('m_fiberSwitch');
Sean Condon4747ece2019-05-04 20:17:02 +0100223 this.is.loadIconDef('m_firewall');
224 this.is.loadIconDef('m_map');
225 this.is.loadIconDef('m_microwave');
226 this.is.loadIconDef('m_ols');
227 this.is.loadIconDef('m_otn');
228 this.is.loadIconDef('m_ports');
229 this.is.loadIconDef('m_resetZoom');
230 this.is.loadIconDef('m_roadm');
231 this.is.loadIconDef('m_roadm_otn');
232 this.is.loadIconDef('m_router');
233 this.is.loadIconDef('m_selectMap');
234 this.is.loadIconDef('m_summary');
235 this.is.loadIconDef('m_switch');
236 this.is.loadIconDef('m_terminal_device');
237 this.is.loadIconDef('m_uiAttached');
238 this.is.loadIconDef('m_unknown');
239 this.is.loadIconDef('meterTable');
240 this.is.loadIconDef('microwave');
241 this.is.loadIconDef('otn');
242 this.is.loadIconDef('portTable');
243 this.is.loadIconDef('roadm_otn');
244 this.is.loadIconDef('triangleUp');
245 this.is.loadIconDef('uiAttached');
Sean Condonf4f54a12018-10-10 23:25:46 +0100246 this.log.debug('Topology component constructed');
247 }
248
Sean Condon91481822019-01-01 13:56:14 +0000249 /**
250 * Static functions must come before member variables
Sean Condonff85fbe2019-03-16 14:28:46 +0000251 * @param index Corresponds to LabelToggle.Enum index
Sean Condon91481822019-01-01 13:56:14 +0000252 */
Sean Condon021f0fa2018-12-06 23:31:11 -0800253 private static deviceLabelFlashMessage(index: number): string {
254 switch (index) {
Sean Condon91481822019-01-01 13:56:14 +0000255 case 0: return 'fl_device_labels_hide';
256 case 1: return 'fl_device_labels_show_friendly';
257 case 2: return 'fl_device_labels_show_id';
Sean Condon021f0fa2018-12-06 23:31:11 -0800258 }
259 }
260
261 private static hostLabelFlashMessage(index: number): string {
262 switch (index) {
Sean Condon91481822019-01-01 13:56:14 +0000263 case 0: return 'fl_host_labels_hide';
264 case 1: return 'fl_host_labels_show_friendly';
265 case 2: return 'fl_host_labels_show_ip';
266 case 3: return 'fl_host_labels_show_mac';
Sean Condon021f0fa2018-12-06 23:31:11 -0800267 }
268 }
269
Sean Condon71910542019-02-16 18:16:42 +0000270 private static gridDisplayFlashMessage(index: number): string {
271 switch (index) {
272 case 0: return 'fl_grid_display_hide';
273 case 1: return 'fl_grid_display_1000';
274 case 2: return 'fl_grid_display_geo';
275 case 3: return 'fl_grid_display_both';
276 }
277 }
278
Sean Condon19e83672019-04-13 16:21:52 +0100279 private static trafficTypeFlashMessage(index: number): string {
280 switch (index) {
281 case 0: return 'tr_fl_fstats_bytes';
282 case 1: return 'tr_fl_pstats_bits';
283 case 2: return 'tr_fl_pstats_pkts';
284 }
285 }
286
Sean Condon91481822019-01-01 13:56:14 +0000287 /**
288 * Pass the list of Key Commands to the KeyService, and initialize the Topology
289 * Service - which communicates with through the WebSocket to the ONOS server
290 * to get the nodes and links.
291 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100292 ngOnInit() {
293 this.bindCommands();
Sean Condon0a884ad2019-10-28 17:57:21 +0000294
Sean Condonaa4366d2018-11-02 14:29:01 +0000295 // The components from the template are handed over to TopologyService here
296 // so that WebSocket responses can be passed back in to them
297 // The handling of the WebSocket call is delegated out to the Topology
298 // Service just to compartmentalize things a bit
299 this.ts.init(this.instance, this.background, this.force);
Sean Condonb2c483c2019-01-16 20:28:55 +0000300
Sean Condon0a884ad2019-10-28 17:57:21 +0000301 // Scale the window initially - then after resize
302 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
303 this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
304 this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
305 this.log.debug('TopologyComponent initialized',
306 this.bannerHeight, this.window.innerWidth, this.window.innerHeight,
307 zoomMapExtents);
308
Sean Condonb0a196a2019-04-19 09:50:44 +0100309 // For the 2.1 release to not listen to updates of prefs as they are
310 // only the echo of what we have sent down and the event mechanism
311 // does not discern between users. Can get confused if multiple windows open
312 // this.ps.addListener((data) => this.prefsUpdateHandler(data));
313
Sean Condonb2c483c2019-01-16 20:28:55 +0000314 this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState);
Sean Condon0d064ec2019-02-04 21:53:53 +0000315 this.mapIdState = this.ps.getPrefs(TOPO_MAPID_PREFS, this.mapIdState);
Sean Condon19e83672019-04-13 16:21:52 +0100316 this.trs.init(this.force);
Sean Condonff85fbe2019-03-16 14:28:46 +0000317 }
318
Sean Condon91481822019-01-01 13:56:14 +0000319 /**
Sean Condonb2c483c2019-01-16 20:28:55 +0000320 * Callback function that's called whenever new Prefs are received from WebSocket
321 *
322 * Note: At present the backend server does not filter updated by logged in user,
323 * so you might get updates pertaining to a different user
324 */
325 prefsUpdateHandler(data: any): void {
326 // Extract the TOPO2 prefs from it
Sean Condon0d064ec2019-02-04 21:53:53 +0000327 if (data[TOPO2_PREFS]) {
328 this.prefsState = data[TOPO2_PREFS];
329 }
Sean Condon0d064ec2019-02-04 21:53:53 +0000330 this.log.debug('Updated topo2 prefs', this.prefsState, this.mapIdState);
Sean Condonb2c483c2019-01-16 20:28:55 +0000331 }
332
333 /**
Sean Condon91481822019-01-01 13:56:14 +0000334 * When this component is being stopped, disconnect the TopologyService from
335 * the WebSocket
336 */
Sean Condonaa4366d2018-11-02 14:29:01 +0000337 ngOnDestroy() {
338 this.ts.destroy();
Sean Condonb2c483c2019-01-16 20:28:55 +0000339 this.ps.removeListener((data) => this.prefsUpdateHandler(data));
Sean Condon19e83672019-04-13 16:21:52 +0100340 this.trs.destroy();
Sean Condonaa4366d2018-11-02 14:29:01 +0000341 this.log.debug('Topology component destroyed');
342 }
343
Sean Condonff85fbe2019-03-16 14:28:46 +0000344 @HostListener('window:resize', ['$event'])
345 onResize(event) {
346 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
347 this.bannerHeight, event.target.innerWidth, event.target.innerHeight);
348 this.zoomDirective.changeZoomLevel(zoomMapExtents, true);
349 this.log.debug('Topology window resize',
350 event.target.innerWidth, event.target.innerHeight, this.bannerHeight, zoomMapExtents);
351 }
352
Sean Condon91481822019-01-01 13:56:14 +0000353 /**
354 * When ever a toolbar button is clicked, an event is sent up from toolbar
355 * component which is caught and passed on to here.
356 * @param name The name of the button that was clicked
357 */
358 toolbarButtonClicked(name: string) {
359 switch (name) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000360 case INSTANCE_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000361 this.toggleInstancePanel();
362 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000363 case SUMMARY_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000364 this.toggleSummary();
365 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000366 case DETAILS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000367 this.toggleDetails();
368 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000369 case HOSTS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000370 this.toggleHosts();
371 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000372 case OFFLINE_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000373 this.toggleOfflineDevices();
374 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000375 case PORTS_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000376 this.togglePorts();
377 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000378 case BKGRND_TOGGLE:
Sean Condon91481822019-01-01 13:56:14 +0000379 this.toggleBackground();
380 break;
Sean Condon0d064ec2019-02-04 21:53:53 +0000381 case BKGRND_SELECT:
382 this.mapSelShown = !this.mapSelShown;
383 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000384 case CYCLELABELS_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000385 this.cycleDeviceLabels();
386 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000387 case CYCLEHOSTLABEL_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000388 this.cycleHostLabels();
389 break;
Sean Condon71910542019-02-16 18:16:42 +0000390 case CYCLEGRIDDISPLAY_BTN:
391 this.cycleGridDisplay();
392 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000393 case RESETZOOM_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000394 this.resetZoom();
395 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000396 case EQMASTER_BTN:
Sean Condon91481822019-01-01 13:56:14 +0000397 this.equalizeMasters();
398 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000399 case CANCEL_TRAFFIC:
Sean Condon91481822019-01-01 13:56:14 +0000400 this.cancelTraffic();
401 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000402 case ALL_TRAFFIC:
Sean Condon19e83672019-04-13 16:21:52 +0100403 this.cycleTrafficTypeDisplay();
Sean Condon91481822019-01-01 13:56:14 +0000404 break;
Sean Condonb2c483c2019-01-16 20:28:55 +0000405 case QUICKHELP_BTN:
406 this.ks.quickHelpShown = true;
407 break;
Sean Condon28884332019-03-21 14:07:00 +0000408 case LAYOUT_DEFAULT_BTN:
409 this.layout.changeLayout(LayoutType.LAYOUT_DEFAULT);
410 break;
411 case LAYOUT_ACCESS_BTN:
412 this.layout.changeLayout(LayoutType.LAYOUT_ACCESS);
413 break;
Sean Condon91481822019-01-01 13:56:14 +0000414 default:
415 this.log.warn('Unhandled Toolbar action', name);
416 }
417 }
418
419 /**
420 * The list of key strokes that will be active in the Topology View.
421 *
422 * This action map is passed to the KeyService through the bindCommands()
423 * when this component is being initialized
Sean Condonbed2e032019-04-17 22:22:49 +0100424 *
425 * TODO - Replace this doggy doo doo (copied over from GUI-1)
426 * with something more structured
Sean Condon91481822019-01-01 13:56:14 +0000427 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100428 actionMap() {
429 return {
Sean Condonbed2e032019-04-17 22:22:49 +0100430 A: [() => {this.cycleTrafficTypeDisplay(); }, this.lionFn('tr_btn_monitor_all')],
431 B: [(token) => {this.toggleBackground(token); }, this.lionFn('tbtt_tog_map')],
432 D: [(token) => {this.toggleDetails(token); }, this.lionFn('tbtt_tog_use_detail')],
433 E: [() => {this.equalizeMasters(); }, this.lionFn('tbtt_eq_master')],
434 H: [() => {this.toggleHosts(); }, this.lionFn('tbtt_tog_host')],
435 I: [(token) => {this.toggleInstancePanel(token); }, this.lionFn('tbtt_tog_instances')],
436 G: [() => {this.mapSelShown = !this.mapSelShown; }, this.lionFn('tbtt_sel_map')],
437 L: [() => {this.cycleDeviceLabels(); }, this.lionFn('tbtt_cyc_dev_labs')],
438 M: [() => {this.toggleOfflineDevices(); }, this.lionFn('tbtt_tog_offline')],
439 O: [() => {this.toggleSummary(); }, this.lionFn('tbtt_tog_summary')],
440 P: [(token) => {this.togglePorts(token); }, this.lionFn('tbtt_tog_porthi')],
441 Q: [() => {this.cycleGridDisplay(); }, this.lionFn('tbtt_cyc_grid_display')],
442 R: [() => {this.resetZoom(); }, this.lionFn('tbtt_reset_zoom')],
443 U: [() => {this.unpinOrFreezeNodes(); }, this.lionFn('tbtt_unpin_node')],
444 X: [() => {this.resetNodeLocation(); }, this.lionFn('tbtt_reset_loc')],
445 dot: [() => {this.toggleToolbar(); }, this.lionFn('tbtt_tog_toolbar')],
446 0: [() => {this.cancelTraffic(); }, this.lionFn('tr_btn_cancel_monitoring')],
447 'shift-L': [() => {this.cycleHostLabels(); }, this.lionFn('tbtt_cyc_host_labs')],
Sean Condonf4f54a12018-10-10 23:25:46 +0100448
449 // -- instance color palette debug
Sean Condon55c30532018-10-29 12:26:57 +0000450 9: () => {
451 this.sus.cat7().testCard(d3.select('svg#topo2'));
Sean Condonf4f54a12018-10-10 23:25:46 +0100452 },
453
Sean Condonbed2e032019-04-17 22:22:49 +0100454 esc: [() => {this.handleEscape(); }, this.lionFn('qh_hint_esc')],
Sean Condonf4f54a12018-10-10 23:25:46 +0100455
456 // TODO update after adding in Background Service
457 // topology overlay selections
458 // F1: function () { t2tbs.fnKey(0); },
459 // F2: function () { t2tbs.fnKey(1); },
460 // F3: function () { t2tbs.fnKey(2); },
461 // F4: function () { t2tbs.fnKey(3); },
462 // F5: function () { t2tbs.fnKey(4); },
463 //
464 // _keyListener: t2tbs.keyListener.bind(t2tbs),
465
466 _helpFormat: [
467 ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
468 ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
469 [], // this column reserved for overlay actions
470 ],
471 };
472 }
473
474
475 bindCommands(additional?: any) {
476
477 const am = this.actionMap();
Sean Condonf4f54a12018-10-10 23:25:46 +0100478 this.ks.keyBindings(am);
479
480 this.ks.gestureNotes([
481 ['click', 'Select the item and show details'],
482 ['shift-click', 'Toggle selection state'],
483 ['drag', 'Reposition (and pin) device / host'],
484 ['cmd-scroll', 'Zoom in / out'],
485 ['cmd-drag', 'Pan'],
486 ]);
487 }
488
489 handleEscape() {
490
491 if (false) {
492 // TODO: Cancel show mastership
493 // TODO: Cancel Active overlay
494 // TODO: Reinstate with components
495 } else {
Sean Condonb2c483c2019-01-16 20:28:55 +0000496 this.nodeSelected(undefined);
Sean Condonf4f54a12018-10-10 23:25:46 +0100497 this.log.debug('Handling escape');
498 // } else if (t2rs.deselectAllNodes()) {
499 // // else if we have node selections, deselect them all
500 // // (work already done)
501 // } else if (t2rs.deselectLink()) {
502 // // else if we have a link selection, deselect it
503 // // (work already done)
504 // } else if (t2is.isVisible()) {
505 // // If the instance panel is visible, close it
506 // t2is.toggle();
507 // } else if (t2sp.isVisible()) {
508 // // If the summary panel is visible, close it
509 // t2sp.toggle();
510 }
511 }
512
Sean Condonb2c483c2019-01-16 20:28:55 +0000513 /**
514 * Updates the cache of preferences locally and onwards to the PrefsService
515 * @param what The attribute of the local topo2-prefs cache to update
516 * @param b the value to update it with
517 */
518 updatePrefsState(what: string, b: number) {
519 this.prefsState[what] = b;
520 this.ps.setPrefs(TOPO2_PREFS, this.prefsState);
Sean Condonf4f54a12018-10-10 23:25:46 +0100521 }
522
Sean Condonb2c483c2019-01-16 20:28:55 +0000523 /**
524 * When the button is clicked on the toolbar or the L key is pressed
525 * 1) cycle through options
526 * 2) flash up a message
527 * 3a) Update the local prefs cache
528 * 3b) And passes on to the global prefs service which sends back to the server
529 * 3c) It also has a knock on effect of passing it on to ForceSvgComponent
530 * because prefsState.dlbls is given as an input to it
531 * 3d) This will in turn pass it down to the DeviceSvgComponent which
532 * displays the label
533 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100534 protected cycleDeviceLabels() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000535 const old: LabelToggle.Enum = this.prefsState.dlbls;
Sean Condon021f0fa2018-12-06 23:31:11 -0800536 const next = LabelToggle.next(old);
Sean Condon91481822019-01-01 13:56:14 +0000537 this.flashMsg = this.lionFn(TopologyComponent.deviceLabelFlashMessage(next));
Sean Condonb2c483c2019-01-16 20:28:55 +0000538 this.updatePrefsState(PREF_DLBLS, next);
Sean Condon021f0fa2018-12-06 23:31:11 -0800539 this.log.debug('Cycling device labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100540 }
541
542 protected cycleHostLabels() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000543 const old: HostLabelToggle.Enum = this.prefsState.hlbls;
Sean Condon021f0fa2018-12-06 23:31:11 -0800544 const next = HostLabelToggle.next(old);
Sean Condon91481822019-01-01 13:56:14 +0000545 this.flashMsg = this.lionFn(TopologyComponent.hostLabelFlashMessage(next));
Sean Condonb2c483c2019-01-16 20:28:55 +0000546 this.updatePrefsState(PREF_HLBLS, next);
Sean Condon021f0fa2018-12-06 23:31:11 -0800547 this.log.debug('Cycling host labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100548 }
549
Sean Condon71910542019-02-16 18:16:42 +0000550 protected cycleGridDisplay() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000551 const old: GridDisplayToggle.Enum = this.prefsState.grid;
Sean Condon71910542019-02-16 18:16:42 +0000552 const next = GridDisplayToggle.next(old);
553 this.flashMsg = this.lionFn(TopologyComponent.gridDisplayFlashMessage(next));
554 this.updatePrefsState(PREF_GRID, next);
555 this.log.debug('Cycling grid display', old, next);
556 }
557
Sean Condon19e83672019-04-13 16:21:52 +0100558 protected cycleTrafficTypeDisplay() {
559 const old: TrafficType.Enum = this.prefsState.traffic; // by number
560 const next = TrafficType.next(old);
561 this.flashMsg = this.lionFn(TopologyComponent.trafficTypeFlashMessage(next));
562 this.updatePrefsState(PREF_TRAFFIC, next);
563 this.trs.requestTraffic(next);
564 this.log.debug('Cycling traffic display', old, next);
565 }
566
Sean Condonb2c483c2019-01-16 20:28:55 +0000567 /**
568 * When the button is clicked on the toolbar or the B key is pressed
569 * 1) Find the inverse of the current state (held as 1 or 0)
570 * 2) Flash up a message on screen
571 * 3b) And passes on to the global prefs service which sends back to the server
572 * 3c) It also has a knock on effect of passing it on to ToolbarComponent
573 * because prefsState.bg is given as an input to it
Sean Condonff85fbe2019-03-16 14:28:46 +0000574 * @param token not currently used
Sean Condonb2c483c2019-01-16 20:28:55 +0000575 */
Sean Condon91481822019-01-01 13:56:14 +0000576 protected toggleBackground(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000577 const bg: boolean = !Boolean(this.prefsState.bg);
578 this.flashMsg = this.lionFn(bg ? 'show' : 'hide') +
Sean Condon91481822019-01-01 13:56:14 +0000579 ' ' + this.lionFn('fl_background_map');
Sean Condonb2c483c2019-01-16 20:28:55 +0000580 this.updatePrefsState(PREF_BG, bg ? 1 : 0);
581 this.log.debug('Toggling background', token, bg ? 'shown' : 'hidden');
Sean Condonf4f54a12018-10-10 23:25:46 +0100582 }
583
Sean Condon91481822019-01-01 13:56:14 +0000584 protected toggleDetails(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000585 const on: boolean = !Boolean(this.prefsState.detail);
586 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
587 ' ' + this.lionFn('fl_panel_details');
588 this.updatePrefsState(PREF_DETAIL, on ? 1 : 0);
589 this.log.debug('Toggling details', token);
Sean Condonf4f54a12018-10-10 23:25:46 +0100590 }
591
Sean Condon91481822019-01-01 13:56:14 +0000592 protected toggleInstancePanel(token?: KeysToken) {
Sean Condonb2c483c2019-01-16 20:28:55 +0000593 const on: boolean = !Boolean(this.prefsState.insts);
Sean Condon91481822019-01-01 13:56:14 +0000594 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
595 ' ' + this.lionFn('fl_panel_instances');
Sean Condonb2c483c2019-01-16 20:28:55 +0000596 this.updatePrefsState(PREF_INSTS, on ? 1 : 0);
Sean Condon91481822019-01-01 13:56:14 +0000597 this.log.debug('Toggling instances', token, on);
Sean Condonf4f54a12018-10-10 23:25:46 +0100598 }
599
600 protected toggleSummary() {
Sean Condonb2c483c2019-01-16 20:28:55 +0000601 const on: boolean = !Boolean(this.prefsState.summary);
Sean Condon91481822019-01-01 13:56:14 +0000602 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
603 ' ' + this.lionFn('fl_panel_summary');
Sean Condonb2c483c2019-01-16 20:28:55 +0000604 this.updatePrefsState(PREF_SUMMARY, on ? 1 : 0);
605 }
606
607 protected togglePorts(token?: KeysToken) {
608 const current: boolean = !Boolean(this.prefsState.porthl);
609 this.flashMsg = this.lionFn(current ? 'enable' : 'disable') +
610 ' ' + this.lionFn('fl_port_highlighting');
611 this.updatePrefsState(PREF_PORTHL, current ? 1 : 0);
612 this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting');
613 }
614
615 protected toggleToolbar() {
616 const on: boolean = !Boolean(this.prefsState.toolbar);
617 this.updatePrefsState(PREF_TOOLBAR, on ? 1 : 0);
618 this.log.debug('toggling toolbar', on ? 'shown' : 'hidden');
619 }
620
621 protected toggleHosts() {
622 const current: boolean = !Boolean(this.prefsState.hosts);
623 this.flashMsg = this.lionFn('hosts') + ' ' +
Sean Condonb0a196a2019-04-19 09:50:44 +0100624 this.lionFn(current ? 'visible' : 'hidden');
Sean Condonb2c483c2019-01-16 20:28:55 +0000625 this.updatePrefsState(PREF_HOSTS, current ? 1 : 0);
626 this.log.debug('toggling hosts: ', this.prefsState.hosts ? 'Show' : 'Hide');
627 }
628
629 protected toggleOfflineDevices() {
630 const on: boolean = !Boolean(this.prefsState.offdev);
631 this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
632 ' ' + this.lionFn('fl_offline_devices');
633 this.updatePrefsState(PREF_OFFDEV, on ? 1 : 0);
634 this.log.debug('toggling offline devices', this.prefsState.offdev);
Sean Condonf4f54a12018-10-10 23:25:46 +0100635 }
636
637 protected resetZoom() {
Sean Condonff85fbe2019-03-16 14:28:46 +0000638 const zoomMapExtents = ZoomUtils.zoomToWindowSize(
639 this.bannerHeight, this.window.innerWidth, this.window.innerHeight);
640 this.zoomDirective.changeZoomLevel(zoomMapExtents, false);
Sean Condon91481822019-01-01 13:56:14 +0000641 this.flashMsg = this.lionFn('fl_pan_zoom_reset');
Sean Condonf4f54a12018-10-10 23:25:46 +0100642 }
643
Sean Condonf4f54a12018-10-10 23:25:46 +0100644 protected equalizeMasters() {
Sean Condon64ea7d22019-04-12 19:39:13 +0100645 this.wss.sendEvent('equalizeMasters', {});
Sean Condon91481822019-01-01 13:56:14 +0000646 this.flashMsg = this.lionFn('fl_eq_masters');
Sean Condonf4f54a12018-10-10 23:25:46 +0100647 this.log.debug('equalizing masters');
Sean Condonf4f54a12018-10-10 23:25:46 +0100648 }
649
Sean Condon9de21352019-04-06 19:22:27 +0100650 /**
651 * If any nodes with fixed positions had been dragged out of place
652 * then put back where they belong
653 * If there are some devices selected reset only these
654 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100655 protected resetNodeLocation() {
Sean Condon9de21352019-04-06 19:22:27 +0100656 const numNodes = this.force.resetNodeLocations();
657 this.flashMsg = this.lionFn('fl_reset_node_locations') +
658 '(' + String(numNodes) + ')';
659 this.log.debug('resetting ', numNodes, 'node(s) location');
Sean Condonf4f54a12018-10-10 23:25:46 +0100660 }
661
Sean Condon9de21352019-04-06 19:22:27 +0100662 /**
663 * Toggle floating nodes between pinned and frozen
664 * If there are floating nodes selected toggle only these
665 */
666 protected unpinOrFreezeNodes() {
667 const pinned: boolean = !Boolean(this.prefsState.pinned);
668 const numNodes = this.force.unpinOrFreezeNodes(pinned);
669 this.flashMsg = this.lionFn(pinned ?
670 'fl_pinned_floating_nodes' : 'fl_unpinned_floating_nodes') +
671 '(' + String(numNodes) + ')';
672 this.updatePrefsState(PREF_PINNED, pinned ? 1 : 0);
673 this.log.debug('Toggling pinning for floating ', numNodes, 'nodes', pinned);
Sean Condonf4f54a12018-10-10 23:25:46 +0100674 }
675
Sean Condon91481822019-01-01 13:56:14 +0000676 /**
677 * Check to see if this is needed anymore
Sean Condonff85fbe2019-03-16 14:28:46 +0000678 * @param what - a key stroke
Sean Condon91481822019-01-01 13:56:14 +0000679 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100680 protected notValid(what) {
681 this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
682 }
683
Sean Condon91481822019-01-01 13:56:14 +0000684 /**
685 * Check to see if this is needed anymore
Sean Condonff85fbe2019-03-16 14:28:46 +0000686 * @param key - a key stroke
Sean Condon91481822019-01-01 13:56:14 +0000687 */
Sean Condonf4f54a12018-10-10 23:25:46 +0100688 getActionEntry(key) {
689 let entry;
690
691 if (!key) {
692 this.notValid('key');
693 return null;
694 }
695
696 entry = this.actionMap()[key];
697
698 if (!entry) {
699 this.notValid('actionMap (' + key + ') entry');
700 return null;
701 }
702 return this.fs.isA(entry) || [entry, ''];
703 }
704
Sean Condon91481822019-01-01 13:56:14 +0000705 /**
706 * An event handler that updates the details panel as items are
707 * selected in the forcesvg layer
Sean Condond88f3662019-04-03 16:35:30 +0100708 *
709 * @param nodesOrLink the item(s) to display details of
Sean Condon91481822019-01-01 13:56:14 +0000710 */
Sean Condond88f3662019-04-03 16:35:30 +0100711 nodeSelected(nodesOrLink: UiElement[]) {
712 this.details.ngOnChanges({'selectedNodes':
713 new SimpleChange(undefined, nodesOrLink, true)});
Sean Condon50855cf2018-12-23 15:37:42 +0000714 }
715
716 /**
717 * Cancel traffic monitoring
718 */
719 cancelTraffic() {
Sean Condon91481822019-01-01 13:56:14 +0000720 this.flashMsg = this.lionFn('fl_monitoring_canceled');
Sean Condon19e83672019-04-13 16:21:52 +0100721 this.trs.cancelTraffic();
Sean Condon50855cf2018-12-23 15:37:42 +0000722 }
Sean Condon91481822019-01-01 13:56:14 +0000723
Sean Condon0d064ec2019-02-04 21:53:53 +0000724 changeMap(map: MapObject) {
725 this.mapSelShown = false; // Hide the MapSelector component
726 this.mapIdState = map;
727 this.ps.setPrefs(TOPO_MAPID_PREFS, this.mapIdState);
728 this.log.debug('Map has been changed to ', map);
729 }
730
Sean Condon1ae15802019-03-02 09:07:18 +0000731 mapExtentsZoom(zoomMapExtents: TopoZoomPrefs) {
732 // this.zoomDirective.updateZoomState(zoomPrefs.tx, zoomPrefs.ty, zoomPrefs.sc);
733 this.zoomDirective.changeZoomLevel(zoomMapExtents);
734 this.log.debug('Map zoom prefs updated', zoomMapExtents);
735 }
736
Sean Condon64ea7d22019-04-12 19:39:13 +0100737 backgroundClicked(event: MouseEvent) {
738 const elemTagName = event.target['tagName'] + ' ' + event.target['id'];
739 if (BACKGROUND_ELEMENTS.includes(elemTagName)) {
740 this.force.updateSelected(
741 <SelectedEvent>{
742 uiElement: undefined,
743 deselecting: true
744 }
745 );
746 }
747 }
748
Sean Condon91481822019-01-01 13:56:14 +0000749 /**
750 * Read the LION bundle for Toolbar and set up the lionFn
751 */
752 doLion() {
753 this.lionFn = this.lion.bundle('core.view.Topo');
754 }
755
756 /**
757 * A dummy implementation of the lionFn until the response is received and the LION
758 * bundle is received from the WebSocket
759 */
760 dummyLion(key: string): string {
761 return '%' + key + '%';
762 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100763}