[CORD-1947] vEE OAM/CFM delay and jitter stat GUI
Change-Id: I82f364ed89805e9be7936adeefc49337917b57fd
diff --git a/xos/veestat/src/app/components/cfmlist.component.html b/xos/veestat/src/app/components/cfmlist.component.html
new file mode 100644
index 0000000..a17afa4
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmlist.component.html
@@ -0,0 +1,82 @@
+<!--
+Copyright 2017-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.
+-->
+
+<div class="row">
+ <div class="col-xs-12">
+ <h1>CFM/OAM Jitter and Delay Monitoring</h1>
+ <p>Select the appropriate MD, MA, MEP, and DM for which you would like to monitor delay and jitter statistics.</p>
+ <a class="btn btn-success"
+ ng-style="{'visibility': vm.displayButton ? 'visible' : 'hidden'}"
+ ng-click="vm.stateChange()"
+ >
+ View Delay and Jitter
+ </a>
+ </div>
+</div>
+
+<div class="row">
+ <div class="col-xs-3">
+ <h4>Maintenance Domains (MD)</h4>
+ </div>
+ <div class="col-xs-3">
+ <h4>Maintenance Associations (MA)</h4>
+ </div>
+ <div class="col-xs-3">
+ <h4>MA Endpoints (MEP)</h4>
+ </div>
+ <div class="col-xs-3">
+ <h4>Delay Measurements (DM)</h4>
+ </div>
+</div>
+
+<div class="row">
+ <div class="col-xs-3">
+ <div class="form-group">
+ <select class="form-control cfmlist" id="md" size="25" ng-model="vm.mdValue" ng-change="vm.triggerRefresh('md')">
+ <option ng-repeat="md in vm.mds" value="{{md.mdNumericId}}">{{md.mdName}} (ID {{md.mdNumericId}})</option>
+ </select>
+ </div>
+ </div>
+ <div class="col-xs-3">
+ <div class="form-group">
+ <select class="form-control cfmlist" id="ma" size="25" ng-model="vm.maValue" ng-change="vm.triggerRefresh('ma')">
+ <option ng-repeat="ma in vm.mas" value="{{ma.maNumericId}}">{{ma.maName}} (ID {{ma.maNumericId}})</option>
+ </select>
+ </div>
+ </div>
+ <div class="col-xs-3">
+ <div class="form-group">
+ <select class="form-control cfmlist" id="mep" size="25" ng-model="vm.mepValue" ng-change="vm.triggerRefresh('mep')">
+ <option ng-repeat="mep in vm.meps" value="{{mep.mepId}}">{{mep.mepId}}</option>
+ </select>
+ </div>
+ </div>
+ <div class="col-xs-3">
+ <div class="form-group">
+ <select class="form-control cfmlist" id="dm" size="25" ng-model="vm.dmValue" ng-change="vm.triggerRefresh('dm')">
+ <!-- <option ng-repeat="dm in vm.dms" value="{{dm.dmId}}">{{dm.dmId}}</option> -->
+ <option value="1" ng-show="vm.displaydms">1</option>
+ <option value="2" ng-show="vm.displaydms">2</option>
+ </select>
+ </div>
+ </div>
+</div>
+
+<div class="row">
+ <div class="col-xs-12">
+ <p>It may take some time for MAs and MEPs to be loaded. Thank you for your patience.</p>
+ </div>
+</div>
\ No newline at end of file
diff --git a/xos/veestat/src/app/components/cfmlist.component.scss b/xos/veestat/src/app/components/cfmlist.component.scss
new file mode 100644
index 0000000..0a42875
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmlist.component.scss
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2017-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.
+ */
+
+.cfmlist > option {
+ color: #fff;
+}
\ No newline at end of file
diff --git a/xos/veestat/src/app/components/cfmlist.component.ts b/xos/veestat/src/app/components/cfmlist.component.ts
new file mode 100644
index 0000000..db1920d
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmlist.component.ts
@@ -0,0 +1,130 @@
+
+/*
+ * Copyright 2017-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 {
+ IMicrosemiMaFormat, IMicrosemiMdFormat, IMicrosemiMepFormat, IMicrosemiStats,
+ IMicrosemiStatsData
+} from './stats-service';
+import './cfmlist.component.scss';
+import * as _ from 'lodash';
+
+class CfmlistComponent {
+
+ static $inject = [
+ 'MicrosemiStats',
+ '$state'
+ ];
+
+ public mds: IMicrosemiMdFormat[];
+ public mas: IMicrosemiMaFormat[];
+ public meps: IMicrosemiMepFormat[];
+ public dms: IMicrosemiStatsData[];
+
+ public md: any;
+ public ma: any;
+ public mdValue: number;
+ public maValue: number;
+ public mepValue: number;
+ public dmValue: number;
+
+ public displayButton = false;
+ public displaydms = false;
+
+ constructor (
+ private MicrosemiStats : IMicrosemiStats,
+ private $state: any
+ ) {}
+
+ $onInit() {
+ this.MicrosemiStats.getMds().then((res) => {
+ this.mds = res;
+ });
+ }
+
+ public triggerRefresh(changed: string) {
+ switch (changed) {
+ case 'md':
+ this.meps = [];
+ this.maValue = null;
+ this.mepValue = null;
+ this.getMas(this.mdValue);
+ this.displayButton = false;
+ this.displaydms = false;
+ break;
+ case 'ma':
+ this.mepValue = null;
+ this.dmValue = null;
+ this.getMeps(this.maValue);
+ this.displayButton = false;
+ this.displaydms = false;
+ break;
+ case 'mep':
+ this.dmValue = null;
+ this.getDms(this.mepValue);
+ this.displayButton = false;
+ this.displaydms = true;
+ break;
+ case 'dm':
+ this.displayButton = true;
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ public getMas(mdNumericId: number) {
+ this.md = _.find(this.mds, (md) => {
+ return md.mdNumericId === Number(mdNumericId);
+ });
+ this.mas = this.md.maList;
+ }
+
+ public getMeps(maNumericId: number) {
+ this.ma = _.find(this.mas, (ma) => {
+ return ma.maNumericId === Number(maNumericId);
+ });
+ this.MicrosemiStats.getMeps(this.md.mdName, this.ma.maName)
+ .then((res: IMicrosemiMepFormat[]) => {
+ this.meps = res;
+ });
+ }
+
+ public getDms(mepNumericId: number) {
+ this.MicrosemiStats.getDms(this.md.mdName, this.ma.maName, String(mepNumericId))
+ .then((res: IMicrosemiStatsData[]) => {
+ this.dms = res;
+ });
+ }
+
+ public stateChange() {
+ this.$state.go('xos.cfmstat', {
+ mdName: this.md.mdName,
+ maName: this.ma.maName,
+ mepId: this.mepValue,
+ dmId: this.dmValue
+ });
+ }
+
+}
+
+export const cfmlistComponent: angular.IComponentOptions = {
+ template: require('./cfmlist.component.html'),
+ controllerAs: 'vm',
+ controller: CfmlistComponent
+};
diff --git a/xos/veestat/src/app/components/cfmstat.component.html b/xos/veestat/src/app/components/cfmstat.component.html
new file mode 100644
index 0000000..8831645
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmstat.component.html
@@ -0,0 +1,82 @@
+
+<!--
+Copyright 2017-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.
+-->
+
+<div class="row">
+ <div class="col-md-12">
+ <h1>Delay and Jitter Stats</h1>
+ </div>
+ <div ng-hide="vm.lineChart.data.length > 0">
+ <div class="loader">
+ Loading
+ </div>
+ </div>
+ <div class="row" ng-show="vm.lineChart.data.length > 0">
+ <div class="col-sm-8">
+ <h3>{{vm.graphTitle}}</h3>
+ <canvas class="chart chart-line" chart-data="vm.lineChart.data" chart-dataset-override="vm.lineChart.dataset"
+ chart-labels="vm.lineChart.labels" chart-series="vm.lineChart.series" chart-options="vm.lineOptions" chart-colors="vm.lineColors">
+ </canvas>
+ <div class="row">
+ <div class="col-xs-2" ng-hide="vm.graphOption == 'delay'">
+ <div class="square" style="background-color: #ffa500"></div>
+ <label>Jitter Max</label>
+ </div>
+ <div class="col-xs-2" ng-hide="vm.graphOption == 'delay'">
+ <div class="square" style="background-color: #c55d01"></div>
+ <label>Jitter Average</label>
+ </div>
+ <div class="col-xs-2" ng-hide="vm.graphOption == 'delay'">
+ <div class="square" style="background-color: #8b0000"></div>
+ <label>Jitter Min</label>
+ </div>
+ <div class="col-xs-2" ng-hide="vm.graphOption == 'jitter'">
+ <div class="square" style="background-color: #add8e6"></div>
+ <label>Delay Max</label>
+ </div>
+ <div class="col-xs-2" ng-hide="vm.graphOption == 'jitter'">
+ <div class="square" style="background-color: #6f6db9"></div>
+ <label>Delay Average</label>
+ </div>
+ <div class="col-xs-2" ng-hide="vm.graphOption == 'jitter'">
+ <div class="square" style="background-color: #00008b"></div>
+ <label>Delay Min</label>
+ </div>
+ </div>
+ <div class="btn-group">
+ <label class="btn btn-primary" ng-model="vm.graphOption" uib-btn-radio="'both'" ng-click="vm.renderGraph()">Delay and Jitter</label>
+ <label class="btn btn-primary" ng-model="vm.graphOption" uib-btn-radio="'delay'" ng-click="vm.renderGraph()">Delay only</label>
+ <label class="btn btn-primary" ng-model="vm.graphOption" uib-btn-radio="'jitter'" ng-click="vm.renderGraph()">Jitter only</label>
+ </div>
+ </div>
+ <div class="col-sm-4">
+ <div class="row">
+ <div class="col-sm-12" style="margin-bottom: 50px">
+ <h3>Current Delay Bin</h3>
+ <canvas class="chart chart-bar" chart-data="vm.delayBin.data" chart-dataset-override="vm.delayBin.dataset"
+ chart-labels="vm.delayBin.labels" chart-series="vm.delayBin.series" chart-options="vm.options">
+ </canvas>
+ </div>
+ <div class="col-sm-12">
+ <h3>Current Jitter Bin</h3>
+ <canvas class="chart chart-bar" chart-data="vm.jitterBin.data" chart-dataset-override="vm.jitterBin.dataset"
+ chart-labels="vm.jitterBin.labels" chart-series="vm.jitterBin.series" chart-options="vm.options">
+ </canvas>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/xos/veestat/src/app/components/cfmstat.component.scss b/xos/veestat/src/app/components/cfmstat.component.scss
new file mode 100644
index 0000000..697b74d
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmstat.component.scss
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright 2017-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.
+ */
+
+.square{
+ width: 10px;
+ height: 10px;
+ display: inline-block;
+ margin-right: 5px;
+}
\ No newline at end of file
diff --git a/xos/veestat/src/app/components/cfmstat.component.ts b/xos/veestat/src/app/components/cfmstat.component.ts
new file mode 100644
index 0000000..e2b78de
--- /dev/null
+++ b/xos/veestat/src/app/components/cfmstat.component.ts
@@ -0,0 +1,212 @@
+
+/*
+ * Copyright 2017-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 {IMicrosemiStats, IMicrosemiStatsData} from './stats-service';
+import './cfmstat.component.scss';
+
+class CfmStatComponent {
+ static $inject = [
+ '$interval',
+ '$stateParams',
+ 'MicrosemiStats'
+ ];
+
+ public options = {
+ animation: {
+ duration: 0
+ },
+ scales: {
+ yAxes: [{
+ ticks: {
+ beginAtZero: true,
+ fontColor: '#fff',
+ },
+ gridLines: {
+ color: '#555',
+ zeroLineColor: '#888'
+ }
+ }],
+ xAxes: [{
+ ticks: {
+ beginAtZero: true,
+ fontColor: '#fff',
+ },
+ gridLines: {
+ color: '#555'
+ }
+ }]
+ },
+
+ };
+
+ public lineOptions = {
+ animation: {
+ duration: 0
+ },
+ scales: {
+ yAxes: [{
+ ticks: {
+ beginAtZero: true,
+ callback: val => `${(Math.round(val * 1000) / 1000).toFixed(3)} ms`,
+ fontColor: '#fff',
+ },
+ gridLines: {
+ color: '#555',
+ zeroLineColor: '#888'
+ }
+ }],
+ xAxes: [{
+ ticks: {
+ beginAtZero: true,
+ fontColor: '#fff',
+ },
+ gridLines: {
+ color: '#555',
+ }
+ }]
+ },
+ bezierCurve: false
+ };
+
+ public lineChart = {
+ series: ['Jitter Avg', 'Jitter Max', 'Jitter Min', 'Delay Avg', 'Delay Max', 'Delay Min'],
+ labels: [],
+ data: [],
+ dataset: []
+ };
+
+ public delayBin = {
+ series: ['Current Delay Bin'],
+ labels: ['0 - 10 ms', '10 - 20 ms', '20 - 37 ms', '> 37 ms'],
+ data: [],
+ dataset: {
+ backgroundColor: '#6f6db9'
+ }
+ };
+
+ public jitterBin = {
+ series: ['Current Jitter Bin'],
+ labels: ['0 - 3 ms', '3 - 8 ms', '8 - 100 ms', '> 100 ms'],
+ data: [],
+ dataset: {
+ backgroundColor: '#ffa500'
+ }
+ };
+
+ public allLineColors = ['#c55d01', '#ffa500', '#8b0000', '#6f6db9', '#add8e6', '#00008b'];
+ public lineColors = this.allLineColors;
+ public lineFill = [
+ {fill: false, borderColor: '#c55d01'},
+ {fill: false, borderColor: '#ffa500'},
+ {fill: false, borderColor: '#8b0000'},
+ {fill: false, borderColor: '#6f6db9'},
+ {fill: false, borderColor: '#add8e6'},
+ {fill: false, borderColor: '#00008b'}];
+
+ public graphOption = 'both';
+ public graphTitle = 'Delay and Jitter Graph';
+ public linedata = [];
+
+ constructor(
+ private $interval: ng.IIntervalService,
+ private $stateParams: ng.ui.IStateParamsService,
+ private MicrosemiStats: IMicrosemiStats,
+ ) {
+
+ }
+
+ $onInit() {
+ const load = () => {
+ this.MicrosemiStats.get(this.$stateParams.mdName, this.$stateParams.maName, this.$stateParams.mepId, this.$stateParams.dmId || 1)
+ .then((res: IMicrosemiStatsData) => {
+ const data = this.MicrosemiStats.getAllData(res);
+
+ this.linedata = data.line.data;
+ this.lineChart.labels = data.line.labels;
+ this.lineChart.data = data.line.data;
+ this.delayBin.data = data.delay.data;
+ this.jitterBin.data = data.jitter.data;
+
+ });
+ };
+
+ load();
+
+ this.renderGraph();
+
+ this.$interval(() => {
+ load();
+ }, 1000 * 60);
+ }
+
+ renderGraph() : void {
+ this.graphTitle = this.graphTitleRender();
+ this.graphDisplayRender();
+ }
+
+ graphTitleRender() : string {
+ let output;
+ switch (this.graphOption) {
+ default:
+ case 'both':
+ output = 'Delay and Jitter Graph';
+ break;
+ case 'delay':
+ output = 'Delay Graph';
+ break;
+ case 'jitter':
+ output = 'Jitter Graph';
+ break;
+ }
+ return output;
+ }
+
+ graphDisplayRender() : void {
+ switch (this.graphOption) {
+
+ default:
+ case 'both':
+ this.lineChart.series = ['Jitter Avg', 'Jitter Max', 'Jitter Min', 'Delay Avg', 'Delay Max', 'Delay Min'];
+ this.lineChart.data = this.linedata;
+ this.lineChart.dataset = this.lineFill;
+ this.lineColors = this.allLineColors;
+ break;
+
+ case 'delay':
+ this.lineChart.series = ['Delay Avg', 'Delay Max', 'Delay Min'];
+ this.lineChart.data = this.linedata.slice(3);
+ this.lineChart.dataset = this.lineFill.slice(3);
+ this.lineColors = this.allLineColors.slice(3);
+ break;
+
+ case 'jitter':
+ this.lineChart.series = ['Jitter Avg', 'Jitter Max', 'Jitter Min'];
+ this.lineChart.data = this.linedata.slice(0, 3);
+ this.lineChart.dataset = this.lineFill.slice(0, 3);
+ this.lineColors = this.allLineColors.slice(0, 3);
+ break;
+ }
+ }
+
+}
+
+
+export const cfmStatComponent: angular.IComponentOptions = {
+ template: require('./cfmstat.component.html'),
+ controllerAs: 'vm',
+ controller: CfmStatComponent
+};
diff --git a/xos/veestat/src/app/components/stats-service.ts b/xos/veestat/src/app/components/stats-service.ts
new file mode 100644
index 0000000..ef506ca
--- /dev/null
+++ b/xos/veestat/src/app/components/stats-service.ts
@@ -0,0 +1,286 @@
+
+/*
+ * Copyright 2017-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 * as _ from 'lodash';
+import IHttpPromiseCallbackArg = angular.IHttpPromiseCallbackArg;
+
+export interface IMicrosemiStatsBinData {
+ lowerLimit: string;
+ count: number;
+}
+
+export interface IMicrosemiStatsBin {
+ bins: IMicrosemiStatsBinData[];
+}
+
+export interface IMicrosemiStatsFormat {
+ interFrameDelayVariationTwoWayAvg: string;
+ interFrameDelayVariationTwoWayMax: string;
+ interFrameDelayVariationTwoWayMin: string;
+ interFrameDelayVariationTwoWayBins: IMicrosemiStatsBin;
+ FrameDelayTwoWayAvg: string;
+ FrameDelayTwoWayMax: string;
+ FrameDelayTwoWayMin: string;
+ FrameDelayTwoWayBins: IMicrosemiStatsBin;
+ [x: string]: any;
+}
+
+export interface IMicrosemiStatsData {
+ dm: {
+ dmId: any;
+ current: IMicrosemiStatsFormat;
+ historic: IMicrosemiStatsFormat[];
+ [x: string]: any;
+ };
+ dmId?: any;
+ current?: IMicrosemiStatsFormat;
+ historic?: IMicrosemiStatsFormat[];
+ [x: string]: any;
+
+}
+
+export interface IMicrosemiChartData {
+ labels: string[];
+ data: [number[]];
+}
+
+export interface IMicrosemiStats {
+ getMds(): any;
+ getMeps(mdName: string, maName: string): any;
+ getDms(mdName: string, maName: string, mepId: string): any;
+ get(mdName: string, maName: string, mepId: string, dmId: string): ng.IPromise<any>;
+ getAllData(res: IMicrosemiStatsData): any;
+}
+
+export interface IMicrosemiMdData {
+ mds?: [IMicrosemiMdFormat[]];
+ md?: IMicrosemiMdFormat;
+}
+
+export interface IMicrosemiMdFormat {
+ mdName: string;
+ mdNameType: string;
+ mdLevel: string;
+ mdNumericId: number;
+ maList: any[];
+}
+
+export interface IMicrosemiMaData {
+ ma: IMicrosemiMaFormat[];
+}
+
+export interface IMicrosemiMaFormat {
+ maName: string;
+ maNameType: string;
+ maNumericId: number;
+ 'ccm-interval': string;
+ componentList: any[];
+ 'rmep-list': any[];
+}
+
+export interface IMicrosemiMepData {
+ meps: [IMicrosemiMepFormat[]];
+}
+
+export interface IMicrosemiMepFormat {
+ mepId: number;
+ deviceId: string;
+ port: string;
+ direction: string;
+ mdName: string;
+ maName: string;
+ 'administrative-state': boolean;
+ 'cci-enabled': boolean;
+ 'ccm-ltm-priority': number;
+ macAddress: string;
+ loopback: any;
+ remoteMeps: any[];
+ activeMacStatusDefect: boolean;
+ activeRdiCcmDefect: boolean;
+
+}
+
+export interface IMicrosemiDmList {
+ dms: [IMicrosemiStatsData[]];
+}
+
+export class MicrosemiStats implements IMicrosemiStats {
+
+ static $inject = [
+ '$q',
+ '$http',
+ '$log',
+ '$base64'
+ ];
+
+ private auth = this.$base64.encode('onos:rocks');
+ private headers = {
+ 'Authorization': `Basic ${this.auth}`
+ };
+
+ constructor (
+ private $q: ng.IQService,
+ private $http: ng.IHttpService,
+ private $log: ng.ILogService,
+ private $base64: any
+ ) {
+
+ }
+
+ public getMds() {
+ const d = this.$q.defer();
+ this.$http.get(`/vtn/onos/cfm/md/`, {'headers': this.headers})
+ .then((res: IHttpPromiseCallbackArg<IMicrosemiMdData>) => {
+ d.resolve(res.data.mds[0]);
+ })
+ .catch(err => {
+ this.$log.error(`[CFMStatService] Error in GET md: `, err);
+ });
+ return d.promise;
+ }
+
+ public getMeps(mdName: string, maName: string) {
+ const d = this.$q.defer();
+ this.$http.get(`/vtn/onos/cfm/md/${mdName}/ma/${maName}/mep/`, {'headers': this.headers})
+ .then((res: IHttpPromiseCallbackArg<IMicrosemiMepData>) => {
+ d.resolve(res.data.meps[0]);
+ })
+ .catch(err => {
+ this.$log.error(`[CFMStatService] Error in GET mep: `, err);
+ });
+ return d.promise;
+ }
+
+ public getDms(mdName : string, maName: string, mepId: string) {
+ const d = this.$q.defer();
+ this.$http.get(`/vtn/onos/cfm/md/${mdName}/ma/${maName}/mep/${mepId}/dm/`, {'headers': this.headers})
+ .then((res: IHttpPromiseCallbackArg<IMicrosemiDmList>) => {
+ d.resolve(res.data.dms[0]);
+ })
+ .catch(err => {
+ this.$log.error(`[CFMStatService] Error in GET dm list: `, err);
+ });
+ return d.promise;
+ }
+
+ public get(mdName : string, maName: string, mepId: string, dmId: string) {
+ const d = this.$q.defer();
+
+ this.$http.get(`/vtn/onos/cfm/md/${mdName}/ma/${maName}/mep/${mepId}/dm/${dmId}`, {'headers': this.headers})
+ .then((res) => {
+ d.resolve(res);
+ })
+ .catch(err => {
+ this.$log.error(`[CFMStatService] Error in GET: `, err);
+ });
+ return d.promise;
+ }
+
+ public getAllData(res: IMicrosemiStatsData) {
+ const data = res['data'];
+ const line = this.parseDataForLine(data);
+ const delay = this.parseDelayBin(data);
+ const jitter = this.parseJitterBin(data);
+ return {
+ line,
+ delay,
+ jitter
+ };
+ }
+
+ private getNumericVal(val: string): number {
+ if (!val) {
+ return 0;
+ }
+ val = val.replace('PT', '');
+ val = val.replace('S', '');
+ return parseFloat(val) * 1000;
+ }
+
+ private formatDate(string: string): string {
+ const date = new Date(string);
+ return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
+ }
+
+ private parseDelayBin(stats: IMicrosemiStatsData) {
+ const labels: number[] = [];
+ const data: number[] = [];
+ _.forEach(stats.dm.current.frameDelayTwoWayBins.bins, (bin) => {
+ labels.push(this.getNumericVal(bin.lowerLimit));
+ data.push(bin.count);
+ });
+ return {
+ labels,
+ data
+ };
+ }
+
+ private parseJitterBin(stats: IMicrosemiStatsData) {
+ const labels: number[] = [];
+ const data: number[] = [];
+ _.forEach(stats.dm.current.interFrameDelayVariationTwoWayBins.bins, (bin) => {
+ labels.push(this.getNumericVal(bin.lowerLimit));
+ data.push(bin.count);
+ });
+ return {
+ labels,
+ data
+ };
+ }
+
+ private parseDataForLine(data: IMicrosemiStatsData): IMicrosemiChartData {
+ let stats = angular.copy(data.dm.historic);
+ stats.unshift(data.dm.current);
+
+ const jitterAvg: number[] = [];
+ const jitterMax: number[] = [];
+ const jitterMin: number[] = [];
+ const delayAvg: number[] = [];
+ const delayMax: number[] = [];
+ const delayMin: number[] = [];
+ const labels: string[] = [];
+
+ _.forEach(stats, (s: IMicrosemiStatsFormat) => {
+ const j_avg = this.getNumericVal(s.interFrameDelayVariationTwoWayAvg);
+ const j_max = this.getNumericVal(s.interFrameDelayVariationTwoWayMax);
+ const j_min = this.getNumericVal(s.interFrameDelayVariationTwoWayMin);
+ const d_avg = this.getNumericVal(s.frameDelayTwoWayAvg);
+ const d_max = this.getNumericVal(s.frameDelayTwoWayMax);
+ const d_min = this.getNumericVal(s.frameDelayTwoWayMin);
+ jitterAvg.push(j_avg);
+ jitterMax.push(j_max);
+ jitterMin.push(j_min);
+ delayAvg.push(d_avg);
+ delayMax.push(d_max);
+ delayMin.push(d_min);
+ labels.push(s.endTime ? this.formatDate(s.endTime) : 'current');
+ });
+
+ return {
+ labels: labels.reverse(),
+ data: [
+ jitterAvg.reverse(),
+ jitterMax.reverse(),
+ jitterMin.reverse(),
+ delayAvg.reverse(),
+ delayMax.reverse(),
+ delayMin.reverse(),
+ ]
+ };
+ }
+
+}
diff --git a/xos/veestat/src/index.html b/xos/veestat/src/index.html
new file mode 100644
index 0000000..6c19ebc
--- /dev/null
+++ b/xos/veestat/src/index.html
@@ -0,0 +1,35 @@
+
+<!--
+Copyright 2017-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.
+-->
+
+
+<!DOCTYPE html>
+<html lang="en" ng-app="veestat">
+<head>
+ <meta charset="UTF-8">
+ <title>veestat</title>
+ <link href="/xos/loader.css" rel="stylesheet">
+ <link href="/xos/app.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>
+</body>
+</html>
diff --git a/xos/veestat/src/index.ts b/xos/veestat/src/index.ts
new file mode 100644
index 0000000..c8bbad3
--- /dev/null
+++ b/xos/veestat/src/index.ts
@@ -0,0 +1,55 @@
+
+/*
+ * Copyright 2017-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.
+ */
+
+
+/// <reference path="../typings/index.d.ts" />
+import * as angular from 'angular';
+
+import 'angular-ui-router';
+import 'angular-resource';
+import 'angular-cookies';
+import 'chart.js';
+import 'angular-chart.js';
+import 'angular-base64';
+
+import routesConfig from './routes';
+import {cfmStatComponent} from './app/components/cfmstat.component';
+import {MicrosemiStats} from './app/components/stats-service';
+import {cfmlistComponent} from './app/components/cfmlist.component';
+
+angular.module('veestat', [
+ 'ui.router',
+ 'app',
+ 'chart.js',
+ 'base64'
+ ])
+ .config(routesConfig)
+ .component('cfmstats', cfmStatComponent)
+ .component('cfmlist', cfmlistComponent)
+ .service('MicrosemiStats', MicrosemiStats)
+ .run(function(
+ $log: ng.ILogService,
+ $state: ng.ui.IStateService,
+ XosNavigationService: any) {
+ $log.info('[CFM Stats] App is running');
+
+ XosNavigationService.add({
+ label: 'CFM Stats',
+ state: 'xos.cfmlist',
+ });
+
+ });
diff --git a/xos/veestat/src/routes.ts b/xos/veestat/src/routes.ts
new file mode 100644
index 0000000..27093b2
--- /dev/null
+++ b/xos/veestat/src/routes.ts
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright 2017-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.
+ */
+
+
+export default routesConfig;
+
+function routesConfig($stateProvider: angular.ui.IStateProvider, $locationProvider: angular.ILocationProvider) {
+ $locationProvider.html5Mode(false).hashPrefix('');
+
+ $stateProvider
+ .state('xos.cfmlist', {
+ url: 'cfmlist',
+ parent: 'xos',
+ component: 'cfmlist'
+ })
+ .state('xos.cfmstat', {
+ url: 'cfmstat',
+ parent: 'xos',
+ component: 'cfmstats',
+ params: {mdName: null, maName: null, mepId: null, dmId: null}
+ });
+}