blob: ea1462ca4d8b186393c79588a3b5d0f868ecb698 [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 Condonf4f54a12018-10-10 23:25:46 +010016import {
Sean Condon91481822019-01-01 13:56:14 +000017 Component,
18 Input,
19 OnChanges,
20 OnDestroy,
21 OnInit,
22 SimpleChanges
23} from '@angular/core';
24import {animate, state, style, transition, trigger} from '@angular/animations';
25import {
Sean Condonf4f54a12018-10-10 23:25:46 +010026 DetailsPanelBaseImpl,
Sean Condon91481822019-01-01 13:56:14 +000027 FnService, LionService,
28 LoadingService,
29 LogService,
Sean Condonf4f54a12018-10-10 23:25:46 +010030 WebSocketService
31} from 'gui2-fw-lib';
Sean Condon91481822019-01-01 13:56:14 +000032import {Host, Link, LinkType, UiElement} from '../../layer/forcesvg/models';
33import {Params, Router} from '@angular/router';
Sean Condonf4f54a12018-10-10 23:25:46 +010034
Sean Condon91481822019-01-01 13:56:14 +000035
36interface ButtonAttrs {
37 gid: string;
38 tt: string;
39 path: string;
40}
41
42const SHOWDEVICEVIEW: ButtonAttrs = {
43 gid: 'deviceTable',
44 tt: 'tt_ctl_show_device',
45 path: 'device',
46};
47const SHOWFLOWVIEW: ButtonAttrs = {
48 gid: 'flowTable',
49 tt: 'title_flows',
50 path: 'flow',
51};
52const SHOWPORTVIEW: ButtonAttrs = {
53 gid: 'portTable',
54 tt: 'tt_ctl_show_port',
55 path: 'port',
56};
57const SHOWGROUPVIEW: ButtonAttrs = {
58 gid: 'groupTable',
59 tt: 'tt_ctl_show_group',
60 path: 'group',
61};
62const SHOWMETERVIEW: ButtonAttrs = {
63 gid: 'meterTable',
64 tt: 'tt_ctl_show_meter',
65 path: 'meter',
66};
67const SHOWPIPECONFVIEW: ButtonAttrs = {
68 gid: 'pipeconfTable',
69 tt: 'tt_ctl_show_pipeconf',
70 path: 'pipeconf',
71};
72
73interface ShowDetails {
74 buttons: string[];
75 glyphId: string;
76 id: string;
77 navPath: string;
78 propLabels: Object;
79 propOrder: string[];
80 propValues: Object;
81 title: string;
82}
83/**
84 * ONOS GUI -- Topology Details Panel.
85 * Displays details of selected device. When no device is selected the panel slides
86 * off to the side and disappears
Sean Condonf4f54a12018-10-10 23:25:46 +010087 */
88@Component({
89 selector: 'onos-details',
90 templateUrl: './details.component.html',
91 styleUrls: [
92 './details.component.css', './details.theme.css',
93 '../../topology.common.css',
94 '../../../../fw/widget/panel.css', '../../../../fw/widget/panel-theme.css'
95 ],
96 animations: [
97 trigger('detailsPanelState', [
98 state('true', style({
99 transform: 'translateX(0%)',
Sean Condon91481822019-01-01 13:56:14 +0000100 opacity: '1.0'
Sean Condonf4f54a12018-10-10 23:25:46 +0100101 })),
102 state('false', style({
103 transform: 'translateX(100%)',
104 opacity: '0'
105 })),
106 transition('0 => 1', animate('100ms ease-in')),
107 transition('1 => 0', animate('100ms ease-out'))
108 ])
109 ]
110})
Sean Condon91481822019-01-01 13:56:14 +0000111export class DetailsComponent extends DetailsPanelBaseImpl implements OnInit, OnDestroy, OnChanges {
112 @Input() selectedNode: UiElement = undefined; // Populated when user selects node or link
Sean Condonb2c483c2019-01-16 20:28:55 +0000113 @Input() on: boolean = false; // Override the parent class attribute
Sean Condon91481822019-01-01 13:56:14 +0000114
115 // deferred localization strings
116 lionFn; // Function
117 showDetails: ShowDetails; // Will be populated on callback. Cleared if nothing is selected
Sean Condonf4f54a12018-10-10 23:25:46 +0100118
119 constructor(
120 protected fs: FnService,
121 protected log: LogService,
122 protected ls: LoadingService,
Sean Condon91481822019-01-01 13:56:14 +0000123 protected router: Router,
Sean Condonf4f54a12018-10-10 23:25:46 +0100124 protected wss: WebSocketService,
Sean Condon91481822019-01-01 13:56:14 +0000125 private lion: LionService
Sean Condonf4f54a12018-10-10 23:25:46 +0100126 ) {
127 super(fs, ls, log, wss, 'topo');
Sean Condon91481822019-01-01 13:56:14 +0000128
129 if (this.lion.ubercache.length === 0) {
130 this.lionFn = this.dummyLion;
131 this.lion.loadCbs.set('flow', () => this.doLion());
132 } else {
133 this.doLion();
134 }
135
136 this.log.debug('Topo DetailsComponent constructed');
Sean Condonf4f54a12018-10-10 23:25:46 +0100137 }
138
Sean Condon91481822019-01-01 13:56:14 +0000139 /**
140 * When the component is initializing set up the handler for callbacks of
141 * ShowDetails from the WSS. Set the variable showDetails when ever a callback
142 * is made
143 */
144 ngOnInit(): void {
Sean Condon91481822019-01-01 13:56:14 +0000145 this.wss.bindHandlers(new Map<string, (data) => void>([
146 ['showDetails', (data) => {
147 this.showDetails = data;
148 // this.log.debug('showDetails received', data);
149 }
150 ]
151 ]));
152 this.log.debug('Topo DetailsComponent initialized');
153 }
154
155 /**
156 * When the component is being unloaded then unbind the WSS handler.
157 */
158 ngOnDestroy(): void {
159 this.wss.unbindHandlers(['showDetails']);
160 this.log.debug('Topo DetailsComponent destroyed');
161 }
162
163 /**
164 * If changes are detected on the Input param selectedNode, call on WSS sendEvent
165 * Note the difference in call to the WSS with requestDetails between a node
166 * and a link - the handling is done in TopologyViewMessageHandler#RequestDetails.process()
167 *
168 * The WSS will call back asynchronously (see fn in ngOnInit())
169 *
170 * @param changes Simple Changes set of updates
171 */
172 ngOnChanges(changes: SimpleChanges): void {
173 if (changes['selectedNode']) {
174 this.selectedNode = changes['selectedNode'].currentValue;
175 let type: any;
176
177 if (this.selectedNode === undefined) {
178 // Selection has been cleared
179 this.showDetails = <ShowDetails>{};
180 return;
181 }
182
183 if (this.selectedNode.hasOwnProperty('nodeType')) { // For Device, Host, SubRegion
184 type = (<Host>this.selectedNode).nodeType;
185 this.wss.sendEvent('requestDetails', {
186 id: this.selectedNode.id,
187 class: type,
188 });
189 } else if (this.selectedNode.hasOwnProperty('type')) { // Must be link
190 const link: Link = <Link>this.selectedNode;
191 if (<LinkType><unknown>LinkType[link.type] === LinkType.UiEdgeLink) { // Number based enum
192 this.wss.sendEvent('requestDetails', {
193 key: link.id,
194 class: 'link',
195 sourceId: link.epA,
196 targetId: Link.deviceNameFromEp(link.epB),
197 targetPort: link.portB,
198 isEdgeLink: true
199 });
200 } else {
201 this.wss.sendEvent('requestDetails', {
202 key: link.id,
203 class: 'link',
204 sourceId: Link.deviceNameFromEp(link.epA),
205 sourcePort: link.portA,
206 targetId: Link.deviceNameFromEp(link.epB),
207 targetPort: link.portB,
208 isEdgeLink: false
209 });
210 }
211 } else {
212 this.log.warn('Unexpected type for selected element', this.selectedNode);
213 }
Sean Condon91481822019-01-01 13:56:14 +0000214 }
215 }
216
217 /**
218 * Table of core button attributes to return per button icon
219 * @param btnName The name of the button
220 * @returns A structure with the button attributes
221 */
222 buttonAttribs(btnName: string): ButtonAttrs {
223 switch (btnName) {
224 case 'showDeviceView':
225 return SHOWDEVICEVIEW;
226 case 'showFlowView':
227 return SHOWFLOWVIEW;
228 case 'showPortView':
229 return SHOWPORTVIEW;
230 case 'showGroupView':
231 return SHOWGROUPVIEW;
232 case 'showMeterView':
233 return SHOWMETERVIEW;
234 case 'showPipeConfView':
235 return SHOWPIPECONFVIEW;
236 default:
237 return <ButtonAttrs>{
238 gid: btnName,
239 path: btnName
240 };
241 }
242 }
243
244 /**
245 * Navigate using Angular Routing. Combines the parameters to generate a relative URL
246 * e.g. if params are 'meter', 'device' and 'null:0000000000001' then the
247 * navigation URL will become "http://localhost:4200/#/meter?devId=null:0000000000000002"
248 *
249 * @param path The path to navigate to
250 * @param navPath The parameter name to use
251 * @param selId the parameter value to use
252 */
253 navto(path: string, navPath: string, selId: string): void {
254 this.log.debug('navigate to', path, 'for', navPath, '=', selId);
255 // Special case until it's fixed
256 if (selId) {
257 if (navPath === 'device') {
258 navPath = 'devId';
259 }
260 const queryPar: Params = {};
261 queryPar[navPath] = selId;
262 this.router.navigate([path], { queryParams: queryPar });
263 }
264 }
265
266 /**
267 * Read the LION bundle for Details panel and set up the lionFn
268 */
269 doLion() {
270 this.lionFn = this.lion.bundle('core.view.Flow');
271
Sean Condonf4f54a12018-10-10 23:25:46 +0100272 }
273
274}