First part of migrating Topo2 to GUI2
Change-Id: I316dd34cba161688e01dfb7b340bff5f2c3c57d4
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
new file mode 100644
index 0000000..5ed744b
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, OnInit, ViewChild } from '@angular/core';
+import {
+ FnService,
+ KeysService, KeysToken,
+ LogService, PrefsService,
+ SvgUtilService, WebSocketService
+} from 'gui2-fw-lib';
+import {InstanceComponent} from '../panel/instance/instance.component';
+import {SummaryComponent} from '../panel/summary/summary.component';
+import {DetailsComponent} from '../panel/details/details.component';
+
+/**
+ * ONOS GUI Topology View
+ *
+ * This Topology View component is the top level component in a hierarchy that
+ * comprises the whole Topology View
+ *
+ * There are three main parts (panels, graphical and breadcrumbs)
+ * The panel hierarchy
+ * |-- Instances Panel (shows ONOS instances)
+ * |-- Summary Panel (summary of ONOS)
+ * |-- Toolbar Panel (the toolbar)
+ * |-- Details Panel (when a node is selected in the Force graphical view (see below))
+ *
+ * The graphical hierarchy contains
+ * Topology (this)
+ * |-- No Devices Connected (only of there are no nodes to show)
+ * |-- Zoom Layer (everything beneath this can be zoomed and panned)
+ * |-- Background (container for any backgrounds - can be toggled on and off)
+ * |-- Map
+ * |-- Forces (all of the nodes and links laid out by a d3.force simulation)
+ *
+ * The breadcrumbs
+ * |-- Breadcrumb (in region view a way of navigating back up through regions)
+ */
+@Component({
+ selector: 'onos-topology',
+ templateUrl: './topology.component.html',
+ styleUrls: ['./topology.component.css']
+})
+export class TopologyComponent implements OnInit {
+ @ViewChild(InstanceComponent) instance: InstanceComponent;
+ @ViewChild(SummaryComponent) summary: SummaryComponent;
+ @ViewChild(DetailsComponent) details: DetailsComponent;
+
+ flashMsg: string = '';
+ prefsState = {};
+ svg: any;
+ hostLabelIdx: number = 1;
+
+ constructor(
+ protected log: LogService,
+ protected fs: FnService,
+ protected ks: KeysService,
+ protected sus: SvgUtilService,
+ protected ps: PrefsService,
+ protected wss: WebSocketService
+ ) {
+
+ this.log.debug('Topology component constructed');
+ }
+
+ ngOnInit() {
+ this.bindCommands();
+ this.log.debug('Topology component initialized');
+ }
+
+ actionMap() {
+ return {
+ L: [() => {this.cycleDeviceLabels(); }, 'Cycle device labels'],
+ B: [(token) => {this.toggleBackground(token); }, 'Toggle background'],
+ D: [(token) => {this.toggleDetails(token); }, 'Toggle details panel'],
+ I: [(token) => {this.toggleInstancePanel(token); }, 'Toggle ONOS Instance Panel'],
+ O: [() => {this.toggleSummary(); }, 'Toggle the Summary Panel'],
+ R: [() => {this.resetZoom(); }, 'Reset pan / zoom'],
+ P: [(token) => {this.togglePorts(token); }, 'Toggle Port Highlighting'],
+ E: [() => {this.equalizeMasters(); }, 'Equalize mastership roles'],
+ X: [() => {this.resetNodeLocation(); }, 'Reset Node Location'],
+ U: [() => {this.unpinNode(); }, 'Unpin node (mouse over)'],
+ H: [() => {this.toggleHosts(); }, 'Toggle host visibility'],
+ M: [() => {this.toggleOfflineDevices(); }, 'Toggle offline visibility'],
+ dot: [() => {this.toggleToolbar(); }, 'Toggle Toolbar'],
+ 'shift-L': [() => {this.cycleHostLabels(); }, 'Cycle host labels'],
+
+ // -- instance color palette debug
+ 9: function () {
+ this.sus.cat7().testCard(this.svg);
+ },
+
+ esc: this.handleEscape,
+
+ // TODO update after adding in Background Service
+ // topology overlay selections
+ // F1: function () { t2tbs.fnKey(0); },
+ // F2: function () { t2tbs.fnKey(1); },
+ // F3: function () { t2tbs.fnKey(2); },
+ // F4: function () { t2tbs.fnKey(3); },
+ // F5: function () { t2tbs.fnKey(4); },
+ //
+ // _keyListener: t2tbs.keyListener.bind(t2tbs),
+
+ _helpFormat: [
+ ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
+ ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
+ [], // this column reserved for overlay actions
+ ],
+ };
+ }
+
+
+ bindCommands(additional?: any) {
+
+ const am = this.actionMap();
+ const add = this.fs.isO(additional);
+
+ // TODO: Reimplement when we have a use case
+ // if (add) {
+ // _.each(add, function (value, key) {
+ // // filter out meta properties (e.g. _keyOrder)
+ // if (!(_.startsWith(key, '_'))) {
+ // // don't allow re-definition of existing key bindings
+ // if (am[key]) {
+ // this.log.warn('keybind: ' + key + ' already exists');
+ // } else {
+ // am[key] = [value.cb, value.tt];
+ // }
+ // }
+ // });
+ // }
+
+ this.ks.keyBindings(am);
+
+ this.ks.gestureNotes([
+ ['click', 'Select the item and show details'],
+ ['shift-click', 'Toggle selection state'],
+ ['drag', 'Reposition (and pin) device / host'],
+ ['cmd-scroll', 'Zoom in / out'],
+ ['cmd-drag', 'Pan'],
+ ]);
+ }
+
+ handleEscape() {
+
+ if (false) {
+ // TODO: Cancel show mastership
+ // TODO: Cancel Active overlay
+ // TODO: Reinstate with components
+ } else {
+ this.log.debug('Handling escape');
+ // } else if (t2rs.deselectAllNodes()) {
+ // // else if we have node selections, deselect them all
+ // // (work already done)
+ // } else if (t2rs.deselectLink()) {
+ // // else if we have a link selection, deselect it
+ // // (work already done)
+ // } else if (t2is.isVisible()) {
+ // // If the instance panel is visible, close it
+ // t2is.toggle();
+ // } else if (t2sp.isVisible()) {
+ // // If the summary panel is visible, close it
+ // t2sp.toggle();
+ }
+ }
+
+
+
+ updatePrefsState(what, b) {
+ this.prefsState[what] = b ? 1 : 0;
+ this.ps.setPrefs('topo2_prefs', this.prefsState);
+ }
+
+ deviceLabelFlashMessage(index) {
+ switch (index) {
+ case 0: return 'Hide device labels';
+ case 1: return 'Show friendly device labels';
+ case 2: return 'Show device ID labels';
+ }
+ }
+
+ hostLabelFlashMessage(index) {
+ switch (index) {
+ case 0: return 'Hide host labels';
+ case 1: return 'Show friendly host labels';
+ case 2: return 'Show host IP labels';
+ case 3: return 'Show host MAC Address labels';
+ }
+ }
+
+ protected cycleDeviceLabels() {
+ this.log.debug('Cycling device labels');
+ // TODO: Reinstate with components
+ // let deviceLabelIndex = t2ps.get('dlbls') + 1;
+ // let newDeviceLabelIndex = deviceLabelIndex % 3;
+ //
+ // t2ps.set('dlbls', newDeviceLabelIndex);
+ // t2fs.updateNodes();
+ // flash.flash(deviceLabelFlashMessage(newDeviceLabelIndex));
+ }
+
+ protected cycleHostLabels() {
+ const hostLabelIndex = this.hostLabelIdx + 1;
+ this.hostLabelIdx = hostLabelIndex % 4;
+ this.flashMsg = this.hostLabelFlashMessage(this.hostLabelIdx);
+ this.log.debug('Cycling host labels');
+ // TODO: Reinstate with components
+ // t2ps.set('hlbls', newHostLabelIndex);
+ // t2fs.updateNodes();
+ }
+
+ protected toggleBackground(token: KeysToken) {
+ this.flashMsg = 'Toggling background';
+ this.log.debug('Toggling background', token);
+ // TODO: Reinstate with components
+ // t2bgs.toggle(x);
+ }
+
+ protected toggleDetails(token: KeysToken) {
+ this.flashMsg = 'Toggling details';
+ this.details.togglePanel(() => {});
+ this.log.debug('Toggling details', token);
+ }
+
+ protected toggleInstancePanel(token: KeysToken) {
+ this.flashMsg = 'Toggling instances';
+ this.instance.togglePanel(() => {});
+ this.log.debug('Toggling instances', token);
+ // TODO: Reinstate with components
+ // this.updatePrefsState('insts', t2is.toggle(x));
+ }
+
+ protected toggleSummary() {
+ this.flashMsg = 'Toggling summary';
+ this.summary.togglePanel(() => {});
+ }
+
+ protected resetZoom() {
+ this.log.debug('resetting zoom');
+ // TODO: Reinstate with components
+ // t2bgs.resetZoom();
+ // flash.flash('Pan and zoom reset');
+ }
+
+ protected togglePorts(token: KeysToken) {
+ this.log.debug('Toggling ports');
+ // TODO: Reinstate with components
+ // this.updatePrefsState('porthl', t2vs.togglePortHighlights(x));
+ // t2fs.updateLinks();
+ }
+
+ protected equalizeMasters() {
+ this.wss.sendEvent('equalizeMasters', null);
+
+ this.log.debug('equalizing masters');
+ // TODO: Reinstate with components
+ // flash.flash('Equalizing master roles');
+ }
+
+ protected resetNodeLocation() {
+ this.log.debug('resetting node location');
+ // TODO: Reinstate with components
+ // t2fs.resetNodeLocation();
+ // flash.flash('Reset node locations');
+ }
+
+ protected unpinNode() {
+ this.log.debug('unpinning node');
+ // TODO: Reinstate with components
+ // t2fs.unpin();
+ // flash.flash('Unpin node');
+ }
+
+ protected toggleToolbar() {
+ this.log.debug('toggling toolbar');
+ // TODO: Reinstate with components
+ // t2tbs.toggle();
+ }
+
+ protected actionedFlashed(action, message) {
+ this.log.debug('action flashed');
+ // TODO: Reinstate with components
+ // this.flash.flash(action + ' ' + message);
+ }
+
+ protected toggleHosts() {
+ // this.flashMsg = on ? 'Show': 'Hide', 'Hosts';
+ this.log.debug('toggling hosts');
+ // TODO: Reinstate with components
+ // let on = t2rs.toggleHosts();
+ // this.actionedFlashed(on ? 'Show': 'Hide', 'Hosts');
+ }
+
+ protected toggleOfflineDevices() {
+ this.log.debug('toggling offline devices');
+ // TODO: Reinstate with components
+ // let on = t2rs.toggleOfflineDevices();
+ // this.actionedFlashed(on ? 'Show': 'Hide', 'offline devices');
+ }
+
+ protected notValid(what) {
+ this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
+ }
+
+ getActionEntry(key) {
+ let entry;
+
+ if (!key) {
+ this.notValid('key');
+ return null;
+ }
+
+ entry = this.actionMap()[key];
+
+ if (!entry) {
+ this.notValid('actionMap (' + key + ') entry');
+ return null;
+ }
+ return this.fs.isA(entry) || [entry, ''];
+ }
+
+}