[CORD-1558] metro-net GUI development
Change-Id: Id85e7cec9d52717777c388f7bfde712bb72850e9
diff --git a/xos/gui/src/app/components/eline-side.component.html b/xos/gui/src/app/components/eline-side.component.html
new file mode 100644
index 0000000..09cf33e
--- /dev/null
+++ b/xos/gui/src/app/components/eline-side.component.html
@@ -0,0 +1,46 @@
+<div>
+ <h1>ELine Overview</h1>
+ <form ng-submit="vm.saveEline(vm.mng.eline)">
+ <div class="form-group" ng-hide="vm.mng.createMode">
+ <label>ID</label><br/>
+ <p>{{vm.mng.eline.id}}</p>
+ </div>
+ <div class="form-group">
+ <label for="name">Name</label>
+ <input required class="form-control" id="name" type="text" ng-value="vm.mng.eline.name" ng-model="vm.mng.eline.name">
+ </div>
+ <div class="form-group" ng-hide="vm.mng.createMode">
+ <label>Backend Status</label><br/>
+ <p>{{vm.mng.eline.backend_status}}</p>
+ </div>
+ <div class="form-group">
+ <label for="cpi1">Connect point 1 ID</label>
+ <input required class="form-control" id="cpi1" type="text" ng-value="vm.mng.eline.connect_point_1_id" ng-model="vm.mng.eline.connect_point_1_id">
+ </div>
+ <div class="form-group">
+ <label for="cpi2">Connect point 2 ID</label>
+ <input required class="form-control" id="cpi2" type="text" ng-value="vm.mng.eline.connect_point_2_id" ng-model="vm.mng.eline.connect_point_2_id">
+ </div>
+ <div class="form-group">
+ <label for="bwps">Bandwidth Profile</label>
+ <select required class="form-control"
+ id="bwps"
+ ng-model="vm.mng.eline.bwp"
+ ng-options="bwp.name as bwp.name for bwp in vm.mng.bwps"
+ >
+ </select>
+ </div>
+ <div class="form-group">
+ <label for="sitename">CORD Site Name</label>
+ <input required class="form-control" id="sitename" type="text" ng-value="vm.mng.eline.cord_site_name" ng-model="vm.mng.eline.cord_site_name">
+ </div>
+ <div class="form-group">
+ <label for="vlanids">Vlan IDs</label>
+ <input required class="form-control" id="vlanids" type="text" ng-value="vm.mng.eline.vlanids" ng-model="vm.mng.eline.vlanids">
+ </div>
+ <div class="form-group" style="text-align: center">
+ <button type="submit" class="btn btn-success btn-block">Save Changes</button>
+ </div>
+ </form>
+ <button type="button" class="btn btn-danger btn-block" ng-click="vm.mng.elinePanel({}, vm.mng.eline, false)">Close</button>
+</div>
\ No newline at end of file
diff --git a/xos/gui/src/app/components/eline-side.component.ts b/xos/gui/src/app/components/eline-side.component.ts
new file mode 100644
index 0000000..ca017c4
--- /dev/null
+++ b/xos/gui/src/app/components/eline-side.component.ts
@@ -0,0 +1,39 @@
+let self;
+
+class ElineSide {
+
+ static $inject = ['XosSidePanel', 'XosModelStore', '$http', '$log', 'toastr'];
+
+ constructor(
+ private XosSidePanel: any,
+ private XosModelStore: any,
+ private $http: any,
+ private $log: any,
+ private toastr: any,
+ ) {
+ self = this;
+ }
+
+ public saveEline(item: any) {
+ let path = item.path;
+ delete item.path;
+ item.$save().then((res) => {
+ item.path = path;
+ this.toastr.success(`${item.name} successfully saved!`);
+ })
+ .catch((error) => {
+ this.toastr.error(`Error while saving ${item.name}: ${error.specific_error}`);
+ });
+ }
+
+
+}
+
+export const elineSide: angular.IComponentOptions = {
+ template: require('./eline-side.component.html'),
+ controllerAs: 'vm',
+ controller: ElineSide,
+ bindings: {
+ mng: '='
+ }
+};
diff --git a/xos/gui/src/app/components/mngMap.component.html b/xos/gui/src/app/components/mngMap.component.html
new file mode 100644
index 0000000..e41422f
--- /dev/null
+++ b/xos/gui/src/app/components/mngMap.component.html
@@ -0,0 +1,61 @@
+<div class = "row">
+ <div class = "col-xs-12">
+ <h1>Metronet Map</h1>
+ <div map-lazy-load="https://maps.googleapis.com/maps/api/js?key=AIzaSyA3rQOp26I5a21VQhwLal8Z1x3XGHjXfm4">
+ <ng-map
+ default-style="false"
+ class="metronet"
+ id="foobar"
+ center="0,0"
+ disable-default-u-i="true"
+ map-type-id="ROADMAP"
+ zoom-control="true"
+ min-zoom="2"
+ styles="{{vm.mapStyles}}"
+ >
+ <!--Markers-->
+ <marker
+ ng-repeat="uni in vm.unis"
+ id="marker-{{uni.id}}"
+ position="{{uni.latlng.toString()}}"
+ icon="{{vm.MapConfig.marker}}"
+ on-click="vm.showUni(uni)"
+ >
+ </marker>
+
+ <!--Marker Info Window-->
+
+ <info-window id = "uni-info">
+ <div class = "marker-info">
+ <h4>{{vm.current_uni.name}}</h4>
+ <p>
+ <b>LatLng: </b>{{vm.current_uni.latlng.toString()}}<br/>
+ <b>Cpe id: </b>{{vm.current_uni.cpe_id}}<br/>
+ <b>Tenant: </b>{{vm.current_uni.tenant}}<br/>
+ </p>
+ <button ng-show="vm.canCreateEline" ng-click="vm.createConnection(vm.current_uni)()">Create connection</button>
+ <button ng-show="!vm.canCreateEline && !uni.eline_start" ng-click="vm.finishConnection(vm.current_uni)">Finish connection</button>
+ </div>
+ </info-window>
+
+ <!--Connections-->
+
+ <shape
+ ng-repeat="eline in vm.elines"
+ name="polyline"
+ id="eline-{{eline.id}}"
+ path="{{eline.path}}"
+ stroke-color="{{vm.colorLine(eline.backend_status)}}"
+ stroke-opacity="1.0"
+ stroke-weight="5"
+ on-click="vm.elinePanel({{eline}}, true)"
+ >
+ </shape>
+
+ </ng-map>
+
+ </div>
+
+ </div>
+</div>
+<!--"https://maps.googleapis.com/maps/api/js?key=AIzaSyA3rQOp26I5a21VQhwLal8Z1x3XGHjXfm4"-->
\ No newline at end of file
diff --git a/xos/gui/src/app/components/mngMap.component.ts b/xos/gui/src/app/components/mngMap.component.ts
new file mode 100644
index 0000000..4cde609
--- /dev/null
+++ b/xos/gui/src/app/components/mngMap.component.ts
@@ -0,0 +1,180 @@
+import {NgMap} from 'ngmap';
+import {Subscription} from 'rxjs/Subscription';
+import * as _ from 'lodash';
+
+declare var google;
+
+let self;
+
+export class MngMap {
+
+ static $inject = [
+ 'NgMap',
+ 'XosModelStore',
+ 'AppConfig',
+ '$resource',
+ 'XosSidePanel',
+ 'XosModelDiscoverer',
+ 'ModelRest',
+ 'MapConfig',
+ ];
+
+ public unis = [];
+ public elines = [];
+ public cpilatlng = new Map();
+ public paths = [];
+ public bwps = [];
+ public map;
+ public panelOpen = false;
+ public createMode = false;
+ public canCreateEline = true;
+ public eline;
+ public current_uni;
+ public mapStyles = [{'featureType': 'administrative', 'elementType': 'labels.text.fill', 'stylers': [{'color': '#444444'}]}, {'featureType': 'landscape', 'elementType': 'all', 'stylers': [{'color': '#f2f2f2'}]}, {'featureType': 'poi', 'elementType': 'all', 'stylers': [{'visibility': 'off'}]}, {'featureType': 'road', 'elementType': 'all', 'stylers': [{'saturation': -100}, {'lightness': 45}]}, {'featureType': 'road.highway', 'elementType': 'all', 'stylers': [{'visibility': 'simplified'}]}, {'featureType': 'road.arterial', 'elementType': 'labels.icon', 'stylers': [{'visibility': 'off'}]}, {'featureType': 'transit', 'elementType': 'all', 'stylers': [{'visibility': 'off'}]}, {'featureType': 'water', 'elementType': 'all', 'stylers': [{'color': '#9ce1fc'}, {'visibility': 'on'}]}];
+
+ private uniSubscription: Subscription;
+ private elineSubscription: Subscription;
+ private bwpSubscription: Subscription;
+
+ constructor(
+ private NgMap: any,
+ private XosModelStore: any,
+ private AppConfig: any,
+ private $resource: any,
+ private XosSidePanel: any,
+ private XosModelDiscoverer: any,
+ private ModelRest: any,
+ private MapConfig: any,
+ ) {
+ self = this;
+ }
+
+ $onInit() {
+ this.NgMap.getMap().then(map => {
+ this.map = map;
+ this.uniSubscription = this.XosModelStore.query('UserNetworkInterface', '/metronet/usernetworkinterfaces/').subscribe(
+ res => {
+ this.unis = res;
+ this.renderMap(map);
+ }
+ );
+ this.elineSubscription = this.XosModelStore.query('ELine', '/metronet/elines/').subscribe(
+ res => {
+ this.elines = res;
+ this.createPaths();
+ this.renderMap(map);
+ }
+ );
+ this.bwpSubscription = this.XosModelStore.query('BandwidthProfile', '/metronet/bandwidthprofiles/').subscribe(
+ res => {
+ this.bwps = res;
+ }
+ );
+ });
+ }
+
+ $onDestroy() {
+ if (this.uniSubscription) {
+ this.uniSubscription.unsubscribe();
+ }
+ }
+
+ public renderMap(map: NgMap) {
+
+ let bounds = new google.maps.LatLngBounds();
+
+ for (let i = 0; i < self.unis.length; i++) {
+ self.unis[i].eline_start = false;
+ let curr = JSON.parse(self.unis[i].latlng);
+ this.cpilatlng.set(self.unis[i].cpe_id, curr);
+ let latlng = new google.maps.LatLng(curr[0], curr[1]);
+ bounds.extend(latlng);
+ }
+ map.setCenter(bounds.getCenter());
+ map.fitBounds(bounds);
+
+ }
+
+ public createPaths() {
+ this.elines.forEach((eline: any) => {
+ let latlng_start = this.cpilatlng.get(eline.connect_point_1_id);
+ let latlng_end = this.cpilatlng.get(eline.connect_point_2_id);
+ eline.path = [latlng_start, latlng_end];
+ });
+
+ }
+
+ public colorLine(eline_status : any) {
+ let status = parseInt(eline_status, 10);
+ switch (status) {
+ case 0:
+ return '#f39c12';
+ case 1:
+ return '#2ecc71';
+ default:
+ return '#e74c3c';
+ }
+
+ }
+
+ public showUni(e: any, uni: any) {
+ self.current_uni = uni;
+ self.map.showInfoWindow('uni-info', this);
+ }
+
+ // do not display backend status or ID in create mode
+
+ public elinePanel(e: any, eline: any, exists: boolean) {
+ self.panelOpen = !self.panelOpen;
+ if (exists) {
+ self.eline = _.find(self.elines, {id: eline.id});
+ }
+ self.XosSidePanel.toggleComponent('elineSide', {eline: self.eline, bwplist: self.bwps, mng: self}, false);
+ if (!self.panelOpen && self.createMode) {
+ self.createMode = false;
+ self.canCreateEline = true;
+ self.current_uni.eline_start = false;
+ }
+
+ }
+
+ public createConnection(uni: any) {
+ return () => {
+ self.canCreateEline = false;
+ self.createMode = true;
+ uni.eline_start = true;
+ self.current_uni = uni;
+ self.eline = {
+ name: uni.name,
+ uni1name: uni.name,
+ connect_point_1_id: uni.cpe_id,
+ };
+ self.elinePanel({}, self.eline, false);
+ };
+
+ }
+
+ public finishConnection(uni: any) {
+ self.eline.connect_point_2_id = uni.cpe_id;
+ if (self.eline.name === self.eline.uni1name) {
+ self.eline.name = self.eline.name + uni.name;
+ }
+ delete self.eline.uni1name;
+ const resource = this.ModelRest.getResource('/metronet/elines/');
+ let res = new resource({});
+ for (let attr in self.eline) {
+ if (true) {
+ res[attr] = self.eline[attr];
+ }
+
+ }
+ self.eline = res;
+ }
+
+}
+
+export const mngMap: angular.IComponentOptions = {
+ template: require('./mngMap.component.html'),
+ controllerAs: 'vm',
+ controller: MngMap,
+};
diff --git a/xos/gui/src/app/img/co.png b/xos/gui/src/app/img/co.png
new file mode 100644
index 0000000..3f07a6a
--- /dev/null
+++ b/xos/gui/src/app/img/co.png
Binary files differ
diff --git a/xos/gui/src/app/style/style.css b/xos/gui/src/app/style/style.css
new file mode 100644
index 0000000..59cdbe1
--- /dev/null
+++ b/xos/gui/src/app/style/style.css
@@ -0,0 +1,12 @@
+ng-map, [map-lazy-load]{
+ height: 600px;
+ width: 100%;
+}
+
+.marker-info > *{
+ color: #000000;
+}
+
+body > ui-view > xos > div > xos-side-panel > section > div:nth-child(1) {
+ display: none;
+}
\ No newline at end of file
diff --git a/xos/gui/src/app/style/style.scss b/xos/gui/src/app/style/style.scss
new file mode 100644
index 0000000..2506519
--- /dev/null
+++ b/xos/gui/src/app/style/style.scss
@@ -0,0 +1,8 @@
+ng-map, [map-lazy-load]{
+ height: 600px;
+ width: 100%;
+}
+
+.marker-info > p, .marker-info > h4{
+ color: #000000;
+}
\ No newline at end of file
diff --git a/xos/gui/src/index.html b/xos/gui/src/index.html
new file mode 100644
index 0000000..a76a8eb
--- /dev/null
+++ b/xos/gui/src/index.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en" ng-app="metro-net-gui">
+<head>
+ <meta charset="UTF-8">
+ <title>Metro Net GUI</title>
+ <link href="/xos/loader.css" rel="stylesheet">
+ <link href="/xos/app.css" rel="stylesheet">
+ <link href="./app/style/style.css" rel="stylesheet">
+</head>
+<body>
+ <div ui-view></div>
+ <script src="/xos/vendor.js"></script>
+ <script src="/xos/app.js"></script>
+ <script src="/xos/loader.js"></script>
+ <script src="/xos/app.config.js"></script>
+ <script src="/xos/style.config.js"></script>
+ <script src="/mapconstants.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/xos/gui/src/index.ts b/xos/gui/src/index.ts
new file mode 100644
index 0000000..e9f070b
--- /dev/null
+++ b/xos/gui/src/index.ts
@@ -0,0 +1,34 @@
+/// <reference path="../typings/index.d.ts" />
+import * as angular from 'angular';
+
+import 'angular-ui-router';
+import 'angular-resource';
+import 'angular-cookies';
+
+import 'ngmap';
+
+import routesConfig from './routes';
+import {mngMap} from './app/components/mngMap.component';
+import {elineSide} from './app/components/eline-side.component';
+
+angular.module('metro-net-gui', [
+ 'ui.router',
+ 'app',
+ 'ngMap'
+ ])
+ .config(routesConfig)
+ .component('mngMap', mngMap)
+ .component('elineSide', elineSide)
+ .run(function(
+ $log: ng.ILogService,
+ $state: ng.ui.IStateService,
+ XosNavigationService: any,
+ XosComponentInjector: any) {
+ $log.info('[metro-net-gui] App is running');
+
+ XosNavigationService.add({
+ label: 'Metronet GUI',
+ state: 'xos.metro-net-gui',
+ });
+
+ });
diff --git a/xos/gui/src/routes.ts b/xos/gui/src/routes.ts
new file mode 100644
index 0000000..51d7217
--- /dev/null
+++ b/xos/gui/src/routes.ts
@@ -0,0 +1,12 @@
+export default routesConfig;
+
+function routesConfig($stateProvider: angular.ui.IStateProvider, $locationProvider: angular.ILocationProvider) {
+ $locationProvider.html5Mode(false).hashPrefix('');
+
+ $stateProvider
+ .state('xos.metro-net-gui', {
+ url: 'metro-net-gui',
+ parent: 'xos',
+ component: 'mngMap'
+ });
+}