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