9a713436fced3f99df8528d3810e7d5346d879c0
[ccsdk/apps.git] / sdnr / wireless-transport / code-Carbon-SR1 / ux / mwtnCommons / mwtnCommons-module / src / main / resources / mwtnCommons / mwtnCommons.services.js
1 /*
2  * @copyright 2017 highstreet technologies GmbH and others. All rights reserved.
3  *
4  * @license
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at {@link http://www.eclipse.org/legal/epl-v10.html} 
8  */
9
10 if (!String.prototype.toHumanReadableTimeFormat) {
11   String.prototype.toHumanReadableTimeFormat = function () {
12     return this.replace(/T/g, ' ').replace(/Z/g, ' UTC').replace(/\+00:00/g, ' UTC');
13   };
14 }
15
16 if (!String.prototype.base64ToHex) {
17   String.prototype.base64ToHex = function () {
18     var raw = atob(this);
19     var result = [];
20     for ( i = 0; i < raw.length; i++ ) {
21       var _hex = raw.charCodeAt(i).toString(16)
22       result.push('0x' + ( _hex.length === 2 ? _hex:'0' + _hex));
23     }
24     return result.join(' ');
25   };
26 }
27
28 if (!String.prototype.contains) {
29   /**
30    * An extension to String, which checks whether another string is contained.
31    * @param {string} find A string to be checked, whether it is contained in 'this'.
32    * @return {boolean} True, if 'this' contains param 'find', otherwise false.
33    */
34   String.prototype.contains = function (find) {
35     return this.indexOf(find) > -1;
36   };
37 }
38
39 if (!String.prototype.format) {
40   /**
41    * An extension to String, which replaces certain patterns by arguments.
42    * @see {@link https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format|javascript-equivalent-to-printf-string-format}
43    * @return {string} Formated string.
44    */
45   String.prototype.format = function () {
46     var args = arguments;
47     return this.replace(/{(\d+)}/g, function (match, number) {
48       return typeof args[number] !== 'undefined' ? args[number] : match
49         ;
50     });
51   };
52 }
53
54 if (!String.prototype.replaceAll) {
55   /**
56    * An extension to String, which replaces certain patterns by arguments.
57    * @see {@link https://stackoverflow.com/questions/1144783/how-to-replace-all-occurrences-of-a-string-in-javascript|how-to-replace-all-occurrences-of-a-string-in-javascript}
58    * @param {string} find - The string which should be replaced.
59    * @param {string} replace - The string which should replace 'find'.
60    * @return {string} String where 'find' is replaced by 'replace'.
61    */
62   String.prototype.replaceAll = function (find, replace) {
63     return this.replace(new RegExp(find, 'g'), replace);
64   }
65 }
66
67 if (!Array.prototype.contains) {
68   /**
69    * An extension to Array checking whether an array of primitive types contains a given value.
70    * @param {string|number|boolean|null|undefined} find An object which should be removed from the array.
71    * @return {boolean} True, if 'this' contains param 'find', otherwise false..
72    */
73   Array.prototype.contains = function (find) {
74     return this.indexOf(find) > -1;
75   };
76 }
77
78 if (!Array.prototype.clean) {
79   /**
80    * An extension to Array removing defined values from an array.
81    * @see {@link https://gist.github.com/waynegraham/3684627|Array.clean()}
82    * @param {Object} deleteValue An object which should be removed from the array.
83    * @return {Array} An array without 'deleteValue'.
84    */
85   Array.prototype.clean = function (deleteValue) {
86     for (var i = 0; i < this.length; i++) { // TODO swtich to .map() ?
87       if (this[i] === deleteValue) {
88         this.splice(i, 1);
89         i--;
90       }
91     }
92     return this;
93   };
94 }
95
96 define(
97   ['app/mwtnCommons/mwtnCommons.module'],
98   function (mwtnCommonsApp) {
99
100     mwtnCommonsApp.register.controller('mwtnFooterController', ['$scope', function ($scope) {
101       var vm = this;
102       $scope.prefix = 'ONF Wireless for OpenDaylight Boron-SR3';
103     }]);
104
105     mwtnCommonsApp.register.directive('mwtnFooter', function () {
106       return {
107         restrict: 'E',
108         controller: 'mwtnFooterController',
109         controllerAs: 'vm',
110         templateUrl: 'src/app/mwtnCommons/templates/mwtnFooter.tpl.html'
111       };
112     });
113
114     mwtnCommonsApp.register.controller('openConfigViewController', ['$scope', '$uibModalInstance', '$mwtnGlobal', '$mwtnCommons', '$mwtnDatabase', '$mwtnLog', 'valueData', 
115       function ($scope, $uibModalInstance, $mwtnGlobal, $mwtnCommons, $mwtnDatabase, $mwtnLog, data) {
116
117       var vm = this;
118       var COMPONENT = 'openConfigViewController';
119
120       $scope.getType = $mwtnGlobal.getType;
121       $scope.spec = data.spec;
122       $mwtnCommons.getConditionalPackagePart($scope.spec).then(function (success) {
123         
124         var getControlType = function(type) {
125           var result = 'text'
126           switch (type) {
127             case 'boolean':
128               result = 'checkbox';
129               break;
130             case 'number':
131               result = 'number';
132               break;
133             case 'string':
134             case 'array':
135             case 'object':
136               break;
137            default:
138               var message = 'Check control type for ' + type;
139               $mwtnLog.warning({ component: COMPONENT, message: message });
140           }
141           return result;
142         };
143
144         success.layerProtocol = $scope.spec.layerProtocolId;
145         $scope.configuredData = success;
146         $scope.newData = $scope.configuredData[$scope.spec.partId];
147
148         $scope.viewData = $mwtnGlobal.getViewData($scope.configuredData[$scope.spec.partId], $scope.ne);
149         $mwtnDatabase.getSchema().then(function(schema){
150           var ordered = {};
151           var clone = JSON.parse(JSON.stringify($scope.viewData));
152           var keys = Object.keys(clone).map(function(key){
153             if ($mwtnGlobal.getType(key) !== 'string') {
154               console.log('key', key);
155               return;
156             }
157             var item = clone[key];
158             if (!schema[key]) {
159               
160               var message = 'No schema information for ' + key;
161               console.error(key, schema[key]);
162               $mwtnLog.warning({ component: COMPONENT, message: message });
163               item['order-number'] = $mwtnCommons.getOrderNumber(97, item);
164               item.description = 'No description available.';
165               item.visible = true;
166               return key;
167             }
168             if (schema[key].controlType === undefined) {
169               item.controlType = getControlType(item.type);
170             } else {
171               item.controlType = schema[key].controlType;
172             }
173             item.unit = schema[key].unit;
174             item.description = schema[key].description;
175             item['order-number'] = $mwtnCommons.getOrderNumber(schema[key]['order-number'], item);
176             if (item.description === undefined || item.description === '') {
177               item.description = 'No description available.';
178             }
179             // hide complex types for now -> TODO
180             if (item.type === 'array' || item.type === 'object') {
181               item.visible = false;
182             }
183             return key;
184           }).sort(function(a,b){
185             if (clone[a]['order-number'] < clone[b]['order-number']) return -1;
186             if (clone[a]['order-number'] > clone[b]['order-number']) return 1;
187             return 0;
188           }).map(function(key){
189             ordered[key] = clone[key];
190           });
191           $scope.viewData = ordered;
192         }, function(error){
193           $scope.empty = true;
194         });
195                   
196       }, function (error) {
197         $scope.configuredData = undefined;
198         $mwtnLog.error({ component: COMPONENT, message: 'Requesting conditional package of ' + JSON.stringify($scope.spec) + ' failed!' });
199       });
200
201       $scope.ok = function () {
202         $scope.processing = true;
203         Object.keys($scope.viewData).map(function(key){
204           $scope.newData[key] = $scope.viewData[key].value;
205         });
206
207         $mwtnCommons.setConditionalPackagePart($scope.spec, $scope.newData).then(function(success){
208           $scope.applied = {text: 'Applied: ' + new Date().toISOString(), class:'mwtnSuccess'};
209           $scope.processing = false;
210         }, function(error){
211           $scope.applied = {text: 'Error: ' + new Date().toISOString(), class:'mwtnError'};
212           $scope.processing = false;
213           $mwtnLog.error({component: COMPONENT, message: JSON.stringify(error)});
214         });
215
216       };
217     
218       $scope.cancel = function () {
219         $uibModalInstance.close($scope.newData);
220       };
221
222     }]);
223
224     mwtnCommonsApp.register.controller('mwtnJsonViewerController', ['$scope', '$uibModal', '$mwtnGlobal', '$mwtnCommons', '$mwtnDatabase', '$mwtnLog',
225       function ($scope, $uibModal, $mwtnGlobal, $mwtnCommons, $mwtnDatabase, $mwtnLog) {
226         var vm = this;
227         var COMPONENT = 'mwtnJsonViewerController';
228         $scope.getType = $mwtnGlobal.getType;
229         
230         if ($scope.data) {
231           $scope.replace = false;
232           if ($scope.path && $scope.path.endsWith('-configuration') ) {
233             $scope.replace = true;
234           }
235           $scope.viewData = $mwtnGlobal.getViewData($scope.data, $scope.ne);
236           var path = [undefined, undefined, undefined];
237           if ($scope.path) {
238             path = $scope.path.split($mwtnCommons.separator);
239           }
240           $scope.spec = {
241             nodeId: $scope.networkElement,
242             revision: '2017-03-20',
243             pacId: path[0],
244             layer: '???',
245             layerProtocolId: path[1],
246             partId: path[2],
247             config: true
248           };
249
250           $scope.openConfigView = function(){
251             var modalInstance = $uibModal.open({
252               animation: true,
253               ariaLabelledBy: 'modal-title',
254               ariaDescribedBy: 'modal-body',
255               templateUrl: 'src/app/mwtnCommons/templates/openConfigView.html',
256               controller: 'openConfigViewController',
257               size: 'huge',
258               resolve: {
259                 valueData: function () {
260                   return {spec:$scope.spec};
261                 }
262               }
263             });
264             modalInstance.result.then(function(object) {
265               // update fetch new data from ODL
266               $mwtnCommons.getPacParts($scope.spec).then(function(success){
267                 // intermedite step to display something, even processing fails or takes time
268                 $scope.viewData = $mwtnGlobal.getViewData(success[$scope.spec.partId], $scope.ne);
269                 // now the nice way ;)
270                 $scope.viewData = processData($scope.schema);
271               }, function(error){
272                 // ignore;
273               });
274             }, function () {
275                 // ignore;
276             });
277           };
278           
279           $scope.myClipboard = {
280             data: [$scope.data],
281             supported: true,
282             getJson: function () {
283               return JSON.stringify(this.data, null, ' ');
284             },
285             copyToClipboard: function () {
286               var message = 'Copied to clipboard! ' + this.getJson();
287               $mwtnLog.info({ component: COMPONENT, message: message });
288             },
289             error: function (err) {
290               $mwtnLog.error({ component: COMPONENT, message: err });
291             }
292           };
293
294           var processData = function(schema) {
295             var ordered = {};
296             var clone = JSON.parse(JSON.stringify($scope.viewData));
297             var keys = Object.keys(clone).map(function(key){
298               if ($mwtnGlobal.getType(key) !== 'string') {
299                 console.log('key', key);
300                 return;
301               }
302               var item = clone[key];
303               if (!schema[key]) {
304                 var message = 'No schema information for ' + key;
305                   $mwtnLog.warning({ component: COMPONENT, message: message });
306                 item['order-number'] = $mwtnCommons.getOrderNumber(97, item);
307                 item.description = 'No description available.';
308                 item.visible = true;
309                 return key;
310               }
311               item.unit = schema[key].unit;
312               item.description = schema[key].description;
313               item['order-number'] = $mwtnCommons.getOrderNumber(schema[key]['order-number'], item);
314               if (item.description === undefined || item.description === '') {
315                 item.description = 'No description available.';
316               }
317               return key;
318             }).sort(function(a,b){
319               if (clone[a]['order-number'] < clone[b]['order-number']) return -1;
320               if (clone[a]['order-number'] > clone[b]['order-number']) return 1;
321               return 0;
322             }).map(function(key){
323               ordered[key] = clone[key];
324             });
325             $scope.info = false;
326             if (Object.keys(ordered).length === 0) {
327               $scope.info = 'An empty object is displayed. Please check if the NetConf server has send an empty object.';
328             }
329             return ordered;
330           };
331
332           $mwtnDatabase.getSchema().then(function(schema){
333             $scope.schema = schema;
334             $scope.viewData = processData($scope.schema);
335           }, function(error){
336             // ignore;
337           });
338         }
339     }]);
340     
341     mwtnCommonsApp.register.directive('mwtnJsonViewer', function () {
342       return {
343         restrict: 'E',
344         scope: {
345           data: '=',
346           path: '=',
347           ne: '=', // flag if ne class
348           networkElement: '=',
349           noButtons: '='
350         },
351         controller: 'mwtnJsonViewerController',
352         controllerAs: 'vm',
353         templateUrl: 'src/app/mwtnCommons/templates/mwtnJsonViewer.tpl.html'
354       };
355     });
356
357     mwtnCommonsApp.register.controller('showGridCellDetailController', ['$scope', '$uibModalInstance', '$mwtnGlobal', 'valueData', 
358                                               function ($scope, $uibModalInstance, $mwtnGlobal, valueData) {
359
360       $scope.networkElement = valueData.networkElement;
361       $scope.path = valueData.path;
362       $scope.type = $mwtnGlobal.getType(valueData.value);
363       $scope.value = valueData.value;
364       // $scope.gridOptions = JSON.parse(JSON.stringify($mwtnCommons.gridOptions));
365       // $scope.highlightFilteredHeader = $mwtnCommons.highlightFilteredHeader;
366
367       console.log('valueData', JSON.stringify(valueData));
368
369       // $scope.ok = function () {
370       //   $scope.processing = true;
371       //   $mwtnCommons.setPacPartLists($scope.path, $scope.listData).then(function(success){
372       //     $scope.applied = {text: 'Applied: ' + new Date().toISOString(), class:'mwtnSuccess'};
373       //     $scope.processing = false;
374       //   }, function(error){
375       //     $scope.applied = {text: 'Error: ' + new Date().toISOString(), class:'mwtnError'};
376       //     $scope.processing = false;
377       //     $mwtnLog.error({component: COMPONENT, message: JSON.stringify(error)});
378       //   });
379
380       // };
381     
382       $scope.cancel = function () {
383         $uibModalInstance.dismiss('cancel');
384       };
385
386     }]);
387
388     mwtnCommonsApp.register.controller('mwtnGridController', ['$scope', '$filter', '$uibModal', '$mwtnGlobal', '$mwtnCommons', '$mwtnLog',
389       function ($scope, $filter, $uibModal, $mwtnGlobal, $mwtnCommons, $mwtnLog) {
390         var vm = this;
391         var COMPONENT = 'mwtnGridController';
392
393         $scope.info = false;
394
395         var data = JSON.parse(JSON.stringify($scope.data));
396         if (!data) {
397           var message = 'No data to be displayed!";'
398           $mwtnLog.info({ component: COMPONENT, message: message });
399           data = [{'message':message}];
400         }
401
402         if ($mwtnGlobal.getType(data) !== 'array')  {
403           var message = 'Data must be of type "array"!';
404           $mwtnLog.info({ component: COMPONENT, message: message });
405           data = [{'message':message}];
406         }
407
408         if (data.length === 0)  {
409           var message = 'Data list must have at least one entry!';
410           $mwtnLog.info({ component: COMPONENT, message: message });
411           data = [{'message':message}];
412         }
413
414         if ($mwtnGlobal.getType(data[0]) !== 'object')  {
415           data = data.map(function(item){
416             return {value: item};
417           });
418         }
419
420         $scope.gridOptions = JSON.parse(JSON.stringify($mwtnCommons.gridOptions));
421         $scope.highlightFilteredHeader = $mwtnCommons.highlightFilteredHeader;
422
423         $scope.getTableHeight = function() {
424           var rowHeight = 30; 
425           var headerHeight = 40; 
426           var maxCount = 12;
427           var rowCount = $scope.gridOptions.data.length + 2;
428           var count = rowCount;
429           if (rowCount > maxCount) {
430             count = maxCount;
431             headerHeight = 31;
432           }
433           return {
434               height: (count * rowHeight + headerHeight) + 'px'
435           };
436         };
437
438         var getCellTemplate = function(field) {
439           var object = ['transmission-mode-list', 'performance-data'];
440           if (object.contains(field)) {
441             console.warn(JSON.stringify(field));
442             return ['<div class="ui-grid-cell-contents">',
443                     '<i class="fa fa-info-circle" aria-hidden="true"',
444                     ' ng-click="grid.appScope.show(grid.getCellValue(row, col))"',
445                     ' style="color: rgb(66, 139, 202); cursor: pointer;"></i> ',
446                     '{{grid.getCellValue(row, col)}}</div>'].join('');
447           }
448           return '<div class="ui-grid-cell-contents">{{grid.getCellValue(row, col)}}</div>';
449         };
450         $scope.show = function(value){
451           // console.warn(JSON.stringify(value));
452           var type = $mwtnGlobal.getType(value);
453           // if (type === 'object')
454           var modalInstance = $uibModal.open({
455             animation: true,
456             ariaLabelledBy: 'modal-title',
457             ariaDescribedBy: 'modal-body',
458             templateUrl: 'src/app/mwtnCommons/templates/showGridCellDetail.html',
459             controller: 'showGridCellDetailController',
460             size: 'huge',
461             resolve: {
462               valueData: function () {
463                 return {networkElement: $scope.networkElement, path:$scope.path, value:value};
464               }
465             }
466           });
467         };
468         var enable = data.length > 10;
469         $scope.gridOptions.columnDefs = Object.keys(data[0]).map(function (field) {
470           var type = $mwtnGlobal.getType(data[0][field]);
471           var labelId = $mwtnGlobal.getLabelId(field);
472           var displayName = $filter('translate')(labelId);
473           var visible = $mwtnGlobal.getVisibilityOf(field);
474           if (labelId.contains('$$') || labelId === 'MWTN_SPEC') {
475             visible = false;
476           }
477           return {
478             field: field,
479             type: type,
480             displayName: displayName,
481             enableSorting: true,
482             enableFiltering: enable,
483             headerCellClass: $scope.highlightFilteredHeader,
484             cellTemplate: getCellTemplate(field),
485             cellClass: type,
486             visible: visible
487           };
488         });
489         if ($scope.gridOptions.data.length < 10) {
490           $scope.gridOptions.minRowsToShow =  data.length; // 10 is default
491         } 
492         $scope.gridOptions.data = data;
493         // .sort(function(a, b){
494         //           if (a.type === 'object') return -1;
495         //           if (a.type === 'array' ) return -2;
496         //           return 0;
497         //         })
498
499         $scope.myClipboard = {
500           data: $scope.data,
501           supported: true,
502           getJson: function () {
503             return JSON.stringify(this.data, null, ' ');
504           },
505           copyToClipboard: function () {
506             var message = 'Copied to clipboard! ' + this.getJson();
507             $mwtnLog.info({ component: COMPONENT, message: message });
508           },
509           error: function (err) {
510             $mwtnLog.error({ component: COMPONENT, message: err });
511           }
512         };
513     }]);
514
515     mwtnCommonsApp.register.directive('mwtnGrid', function () {
516       return {
517         restrict: 'E',
518         scope: {
519           data: '=',
520           path: '=',
521           networkElement: '='
522         },
523         controller: 'mwtnGridController',
524         controllerAs: 'vm',
525         templateUrl: 'src/app/mwtnCommons/templates/mwtnGrid.tpl.html'
526       };
527     });
528
529     mwtnCommonsApp.register.controller('mwtnSelectNetworkElementController', ['$scope', '$state','$mwtnCommons', function ($scope, $state, $mwtnCommons) {
530       var vm = this;
531       
532       /**
533        * A function which scanns the mountpoints for connected network-elements and adds it to networkElements.
534        * @param {{"onfAirInterfaceRevision": string, "node-id": string, "netconf-node-topology:connection-status": string}[]} mountpoints An array of mountpoints from OpenDaylight.
535        */
536       var initNodeList = function (mountpoints) {
537         $scope.loading = true;
538         $scope.mountPoints = mountpoints;
539         $scope.networkElements = mountpoints.filter(function (mountpoint) {
540           return mountpoint['netconf-node-topology:connection-status'] === 'connected';
541         }).map(function (mountpoint) {
542           return { id: mountpoint['node-id'], revision: mountpoint.onfAirInterfaceRevision };
543         }).sort(function (a, b) {
544           if (a.id < b.id) return -1;
545           if (a.id > b.id) return 1;
546           return 0;
547         });
548
549         
550         $scope.networkElement = undefined;
551         if ($state.params.nodeId) {
552           
553         }
554         
555         console.error('nodeId', $state.params.nodeId);
556
557         // select one of the nodes
558         /* dont do it in field applications!!!! 
559         var select = parseInt(Math.random() * $scope.networkElements.length);
560         if (select !== undefined && $scope.networkElements[select]) {
561           $scope.networkElement = $scope.networkElements[select].id;
562           $scope.mountpoint = $scope.mountPoints.filter(function (mountpoint) {
563             return mountpoint['node-id'] === $scope.networkElement;
564           })[0];
565         }
566         */
567         $scope.loading = false;
568       };
569
570       $mwtnCommons.getMountPoints().then(function (mountpoints) {
571         initNodeList(mountpoints);
572       }, function (error) {
573         $scope.networkElements = [];
574       });
575     }]);
576
577     mwtnCommonsApp.register.directive('mwtnSelectNetworkElement', function () {
578       return {
579         restrict: 'E',
580         controller: 'mwtnSelectNetworkElementController',
581         controllerAs: 'vm',
582         templateUrl: 'src/app/mwtnCommons/templates/mwtnSelectNetworkElement.tpl.html'
583       };
584     });
585
586     // mwtnCommonsApp.register.factory('$notifying', function($rootScope) {
587     //   return {
588     //       subscribe: function(scope, callback) {
589     //           var handler = $rootScope.$on('notifying-service-event', callback);
590     //           scope.$on('$destroy', handler);
591     //       },
592
593     //       notify: function(text) {
594     //           $rootScope.$emit('notifying-service-event', text);
595     //       }
596     //   };
597     // });
598
599     mwtnCommonsApp.register.factory('$mwtnGlobal', function () {
600       var service = {};
601
602       /** 
603        * Returns false, if parameter/attribute should be in visible
604        * @param {string} field - a json to be analyzed
605        * @return {boolean} true, if the parameter should be visible, otherwise false.
606        */
607       service.getVisibilityOf = function(field) {
608         var hide = ['name-binding', 'object-class'];
609         return !hide.contains(field);
610       };
611
612       /** 
613        * Returns the (json) type of a value
614        * @param {*} value - a json to be analyzed
615        * @return type of json
616        */
617       service.getType = function (value) {
618         var result = typeof value;
619         if (result === 'object' && JSON.stringify(value).substring(0, 1) === '[') {
620           result = 'array';
621         } else if (result === 'object' && value === null) {
622           result = 'null';
623         } else if (result === 'object' && value['value-name'] && value.value) {
624           result = 'name-value';
625         }
626         return result;
627       };
628
629       service.getLabelId = function (string) {
630         return ['mwtn', string].join('_').replaceAll('-', '_').toUpperCase();
631       };
632
633       var isIntesting = function(key) {
634         var instesting = ['name', 'local-id', 'label', 'extension', 'physical-port-reference', 'lp', 'server-ltp', 'client-ltp', 'layer-protocol-name'];
635         return instesting.contains(key);
636       }
637       /**
638        * Returns a simplfies json  for display
639        * @param {*} data - the json to be simplified
640        * @param {boolean} ne - control parameter for finetuning
641        * @return a simplfied json for display
642        */
643       service.getViewData = function (data, ne) {
644         var viewData = JSON.parse(JSON.stringify(data));
645         if (ne) {
646           viewData.ltp = undefined; 
647           viewData.fd = undefined;
648         }
649         Object.keys(viewData).map(function (key) {
650           var type = service.getType(viewData[key]);
651           var viewValue = { 
652             value: viewData[key], 
653             type: type, 
654             labelId: service.getLabelId(key),
655             visible: service.getVisibilityOf(key)
656           };
657           viewData[key] = viewValue;
658           if (type === 'array') {
659             if (key === 'extension') {
660               viewValue.value.map(function (item) {
661                 viewData[item['value-name']] = { value: item.value, type: 'string', labelId:service.getLabelId(item['value-name']) };
662               });
663               viewData[key] = undefined;
664             } else if (viewValue.value.length === 1 && isIntesting(key)) {
665               // console.warn(key, JSON.stringify(viewData[key]));
666               var valueType = service.getType(viewValue.value[0]);
667               viewData[key].value = viewValue.value[0];
668               viewData[key].type = valueType;
669
670               if (valueType === 'object') {
671                 viewData[key].value = service.getViewData(viewValue.value);
672               } else if (valueType === 'name-value') {
673                 viewData[key].value = viewValue.value.value;
674                 //viewData[key].type = 'string';
675               }
676             } else if (type === 'object') {
677               viewData[key].value = service.getViewData(viewValue.value);
678             }
679           }
680         });
681         return viewData;
682       };
683       return service;
684     });
685
686     /**
687       * Process an array of data synchronously.
688       * Please see https://gist.github.com/KevinTCoughlin/6901825 
689       * @param {Array} data An array of data.
690       * @param {function} processData A function that processes an item of data.
691       *        Signature: function(item, i, callback), where {@code item} is the i'th item,
692       *                   {@code i} is the loop index value and {@code calback} is the
693       *                    parameterless function to call on completion of processing an item.
694       * @param {function} done A callback function indecating that all items are processed.
695       */
696     var doSynchronousLoop = function (data, processData, done) {
697       if (data && data.length > 0) {
698         var loop = function (data, i, processData, done) {
699           processData(data[i], i, function () {
700             if (++i < data.length) {
701               loop(data, i, processData, done);
702             } else {
703               done();
704             }
705           });
706         };
707         loop(data, 0, processData, done);
708       } else {
709         done();
710       }
711     };
712
713     mwtnCommonsApp.register.factory('$mwtnCommons', function ($http, $q, $mwtnGlobal, $mwtnLog, $mwtnDatabase, LogicalTerminationPoint, PtpClock) {
714
715       var COMPONENT = '$mwtnCommons';
716
717       var service = {
718         base: window.location.origin + "/restconf/",
719         database: {},
720         'sdn-controller': [],
721         modules: {},
722         layerProtocolNameOrder: {
723           'MWPS': 6,
724           'MWS': 5,
725           'ETC': 4,
726           'TDM': 3,
727           'ETY': 2,
728           'ETH-CTP': 1,
729           'ETH': 1,
730         }
731       };
732
733       service.getOrderNumber = function(proposal, item){
734         var result = proposal;
735         if (item.type === 'array') {
736           proposal = 99;
737         } else if (item.type === 'object') {
738           proposal = 98;
739         } else if (item.labelId === 'MWTN_LOCAL_ID') {
740           proposal = -1;
741         } else if (item.labelId === 'MWTN_LABEL') {
742           proposal = -2;
743         } else if (item.labelId === 'MWTN_NAME') {
744           proposal = -3;
745         } else if (item.labelId === 'MWTN_UUID') {
746           proposal = -4;
747         }
748         return proposal;
749       }
750
751       /**
752        * A function to get the global-identifier of a condtional package subClass by a specifcation object of such subClass and a local-name.
753        * @param {{pacId: string, layerProtocolId: string, partId:string}} spec - Specification object of a conditional package subclass.
754        * @param {string} local-name (e.g. current-problems)
755        * @return {string} globel-identifier of a conditional packages subClass (e.g. 'air-interface-diversity-current-problems')
756        */
757       service.getPartGlobalId = function (spec, localName) {
758         var address = spec.pacId.split(':');
759         var module = address[0];
760         var pacName = address[1];
761
762         // check for unexpected issues
763         if (!module) {
764           console.error('not module', module, JSON.stringify(spec));
765           return;
766         }
767         if (!pacName) {
768           console.error('not pacName', pacName, JSON.stringify(spec));
769           return;
770         }
771         if (!service.modules[module]) {
772           console.error('not service.modules[module]', service.modules[module], JSON.stringify(spec));
773           return;
774         }
775         if (!service.modules[module][pacName]) {
776           console.error('not service.modules[module][pacName]', service.modules[module][pacName], JSON.stringify(spec));
777           return;
778         }
779         if (!service.modules[module]) {
780           console.error('not ervice.modules[module][pacName][subClass]', service.modules[module][pacName][subClass], JSON.stringify(spec));
781           return;
782         }
783         
784         return Object.keys(service.modules[module][pacName]).filter(function (subClass) {
785           return service.modules[module][pacName][subClass]['local-name'] === localName;
786         }).map(function (subClass) {
787           return service.modules[module][pacName][subClass].name;
788         })[0];
789       
790       };
791
792       /**
793        * A function to get the local-name of a condtional package subClass by a specifcation object of such subClass.
794        * @param {{pacId: string, layerProtocolId: string, partId:string}} spec - Specification object of a conditional package subclass
795        * @return {string} local-name of a conditional packages subClass (e.g. 'current-problems')
796        */
797       service.getPartLocalId = function (spec) {
798         var result;
799         Object.keys(service.modules).map(function (module) {
800           Object.keys(service.modules[module]).filter(function (pacName) {
801             return spec.pacId === [module, pacName].join(':') && service.modules[module][pacName][spec.partId].name === spec.partId;
802           }).map(function (pacName) {
803             result = service.modules[module][pacName][spec.partId]['local-name'];
804           });
805         });
806         return result;
807       };
808
809       var init = function () {
810         var deferred = $q.defer();
811         var databaseRequest = {
812           base: $mwtnDatabase.base,
813           index: 'config',
814           docType: 'sdn-controller',
815           command: '_search',
816           method: 'GET',
817           from: 0,
818           size: 10
819         };
820         $mwtnDatabase.genericRequest(databaseRequest).then(function (success) {
821           // console.log('sc', JSON.stringify(success.data.hits.hits));
822           service['sdn-controller'] = success.data.hits.hits;
823           $mwtnDatabase.getModules().then(function (success) {
824             service.modules = success;
825           }, function (error) {
826             service.modules = [];
827             console.error('modules', JSON.stringify(error));
828             deferred.reject(error);
829           });
830           deferred.resolve();
831         }, function (error) {
832           service['sdn-controller'] = [];
833           console.error('sc', JSON.stringify(error));
834           deferred.reject(error);
835         });
836
837         return deferred.promise;
838       };
839
840       service.getMainConroller = function () {
841         var deferred = $q.defer();
842         if (service['sdn-controller'].length === 0) {
843           init().then(function (success) {
844             service['sdn-controller'].map(function (controller) {
845               result = controller;
846             });
847             deferred.resolve(result);
848           }, function (error) {
849             deferred.reject(error);
850           });
851         } else {
852           service['sdn-controller'].map(function (controller) {
853             result = controller;
854           });
855           deferred.resolve(result);
856         }
857         return deferred.promise;
858       };
859
860       var createStream = function (streamName, callback) {
861         service.getMainConroller().then(function (success) {
862           var src = success._source;
863           var ip = src.host;
864           if (ip === 'localhost') {
865             ip = service.base.split('//')[1].split(':')[0];
866           }
867           var url = [src['transport-protocol'], '://', ip, ':', src.port, '/restconf/streams/stream/', streamName].join('');
868           var request = {
869             method: 'GET',
870             url: url
871           };
872           $http(request).then(function (response) {
873             // console.log(response.headers('Location'));
874             callback(response.headers('Location'));
875           }, function errorCallback(response) {
876             console.error(JSON.stringify(response));
877             callback();
878           });
879         }, function (error) {
880           console.error('mainController', error);
881           callback();
882         });
883       };
884
885       service.getMwtnWebSocketUrl = function () {
886         var deferred = $q.defer();
887         service.getMainConroller().then(function (success) {
888
889           var protocol = window.location.protocol.replace(/http/g, 'ws');
890           var host = window.location.hostname;
891           var user = window.localStorage.odlUser;
892           if (user === undefined) user = 'admin' // ODL default user
893           var pw = window.localStorage.odlPass;
894           if (pw === undefined) pw = 'admin' // ODL default password
895   
896           var url = [protocol, '//', user, ':',pw, '@', host, ':8085/websocket'].join('');
897           console.info('url', url);
898
899           deferred.resolve(url);
900         }, function (error) {
901           console.error('mainController', error);
902           deferred.reject(error);
903         });
904         return deferred.promise;
905       };
906
907       service.formatTimeStamp = function (t) {
908         // t: time in ONF format, e.g. 20161020081633.7Z, 20161025235946.0+0000
909         if (t.length !== '20161020081633.7Z'.length || t.length !== '20161025221822.0+0000') {
910           if (t.endsWith('Z') || t.endsWith('+0000')) {
911             if (!t.contains('-')) {
912               return [[t.slice(0, 4), t.slice(4, 6), t.slice(6, 8)].join('-'),
913               [t.slice(8, 10), t.slice(10, 12), t.slice(12, 16)].join(':')].join(' ') + ' UTC';
914             }
915           }
916         }
917         // console.info('check', t);
918         // return new Date().toISOString().toHumanReadableTimeFormat();
919         return t.toHumanReadableTimeFormat();
920       };
921
922       service.formatData = function (event) {
923         var deferred = $q.defer();
924
925         var x2js = new X2JS();
926         var jsonObj = x2js.xml_str2json(event.data);
927         // console.info('a', service.getType(jsonObj), JSON.stringify(jsonObj));
928         if (jsonObj === null || service.getType(jsonObj) !== 'object') {
929           deferred.reject('ignore');
930         } else {
931           notifType = Object.keys(jsonObj)[0];
932           var formated = jsonObj[notifType];
933           formated.timeStamp = service.formatTimeStamp(formated.timeStamp);
934           formated.notifType = notifType;
935           formated.myMessage = 'someMessage';
936           formated.time = new Date().toISOString();
937           deferred.resolve(formated);
938         }
939
940         return deferred.promise;
941       };
942
943       service.getData = function (callback) {
944         return callback('$mwtnCommons registered to this application.');
945       };
946
947       service.getType = $mwtnGlobal.getType;
948       service.getViewData = $mwtnGlobal.getViewData;
949
950       service.getLayer = function (pacId) {
951         console.warn('@depricated', '$mwtnCommons.getLayer()');
952         switch (pacId) {
953           case 'airinterface':
954           case 'air-interface':
955             return 'MWPS';
956           case 'structure':
957           case 'pureEthernetStructure':
958           case 'hybridStructure':
959           case 'pure-ethernet-structure':
960           case 'hybrid-structure':
961             return 'MWS';
962           case 'container':
963           case 'ethernetContainer':
964           case 'ethernet-container':
965             return 'ETC';
966           case 'tdmContainer':
967           case 'tdm-container':
968             return 'TDM';
969           default:
970             return (pacId);
971         }
972       };
973
974       /** @deprecated */
975       service.parts = ['Capability', 'Configuration', 'Status', 'CurrentProblems', 'CurrentPerformance', 'HistoricalPerformances'];
976
977       service.getLabelId = function (key, callback) {
978         return callback(['mwtn', key].join('_').toUpperCase());
979       };
980
981       service.checkModules = function (names) {
982         // accepts a list of module names and
983         // attempts to load them, in order.
984         // attempt to load the module into m
985         var m;
986         var result = {};
987         names.map(function (name) {
988           try {
989             m = angular.module(name);
990             result[name] = true;
991           } catch (err) {
992             result[name] = false;
993           }
994         });
995         return result;
996       };
997
998       service.mount = function (mp) {
999         // mp: mounting point
1000         var url = [service.base, service.url.mount(mp.name)].join('');
1001         /* deprecated 
1002         var xml = [
1003           '<module xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">',
1004           '<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">prefix:sal-netconf-connector</type>',
1005           '<name>{0}</name>',
1006           '<address xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">{1}</address>',
1007           '<port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">{2}</port>',
1008           '<username xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">{3}</username>',
1009           '<password xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">{4}</password>',
1010           '<tcp-only xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">false</tcp-only>',
1011           '<event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">',
1012           '  <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>',
1013           '  <name>global-event-executor</name>',
1014           '</event-executor>',
1015           '<binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">',
1016           '  <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>',
1017           '  <name>binding-osgi-broker</name>',
1018           '</binding-registry>',
1019           '<dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">',
1020           '  <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>',
1021           '  <name>dom-broker</name>',
1022           '</dom-registry>',
1023           '<client-dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">',
1024           '  <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>',
1025           '  <name>global-netconf-dispatcher</name>',
1026           '</client-dispatcher>',
1027           '<processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">',
1028           '  <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>',
1029           '  <name>global-netconf-processing-executor</name>',
1030           '</processing-executor>',
1031           '<keepalive-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">',
1032           '  <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>',
1033           '  <name>global-netconf-ssh-scheduled-executor</name>',
1034           '</keepalive-executor>', 
1035           '</module>' ].join('').format(mp.name, mp.ipaddress, mp.port, mp.username, mp.password); */
1036         var xml = [
1037           '<node xmlns="urn:TBD:params:xml:ns:yang:network-topology">',
1038           '  <node-id>{0}</node-id>',
1039           '  <host xmlns="urn:opendaylight:netconf-node-topology">{1}</host>',
1040           '  <port xmlns="urn:opendaylight:netconf-node-topology">{2}</port>',
1041           '  <username xmlns="urn:opendaylight:netconf-node-topology">{3}</username>',
1042           '  <password xmlns="urn:opendaylight:netconf-node-topology">{4}</password>',
1043           '  <tcp-only xmlns="urn:opendaylight:netconf-node-topology">false</tcp-only>',
1044
1045           '  <!-- non-mandatory fields with default values, you can safely remove these if you do not wish to override any of these values-->',
1046           '  <reconnect-on-changed-schema xmlns="urn:opendaylight:netconf-node-topology">false</reconnect-on-changed-schema>',
1047           '  <connection-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">20000</connection-timeout-millis>',
1048           '  <max-connection-attempts xmlns="urn:opendaylight:netconf-node-topology">100</max-connection-attempts>',
1049           '  <between-attempts-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">2000</between-attempts-timeout-millis>',
1050           '  <sleep-factor xmlns="urn:opendaylight:netconf-node-topology">1.5</sleep-factor>',
1051
1052           '  <!-- keepalive-delay set to 0 turns off keepalives-->',
1053           '  <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology">120</keepalive-delay>',
1054           '</node>'].join('').format(mp.name, mp.ipaddress, mp.port, mp.username, mp.password);
1055
1056         var request = {
1057           method: 'PUT',
1058           url: url,
1059           headers: {
1060             'Content-Type': 'application/xml',
1061             'Accept': 'application/xml'
1062           },
1063           data: xml
1064         };
1065         var deferred = $q.defer();
1066         $http(request).then(function (success) {
1067           deferred.resolve(success.data);
1068         }, function (error) {
1069           $mwtnLog.error({ component: '$mwtnCommons.mount', message: JSON.stringify(error.data) });
1070           deferred.reject(error);
1071         });
1072         return deferred.promise;
1073       };
1074
1075       /**
1076        * A promise to unmount netconf devices the old way from OpenDaylight Lithium. 
1077        * This is needed in case Netconf devices from 3rd ONF Wireless PoC were mounted.
1078        * @param {string} nodeId - The mountpoint identifier which should be unmounted the old way.
1079        */
1080       var unmountDeprecated = function (nodeId) {
1081         var url = [service.base,
1082           'config/network-topology:network-topology/topology/topology-netconf/node/controller-config/yang-ext:mount/config:modules/module/odl-sal-netconf-connector-cfg:sal-netconf-connector/',
1083           nodeId].join('');
1084         var request = {
1085           method: 'DELETE',
1086           url: url
1087         };
1088         var deferred = $q.defer();
1089         $http(request).then(function (success) {
1090           $mwtnLog.info({ component: COMPONENT, message: 'Mounting Point deleted: ' + nodeId });
1091           deferred.resolve(success.data);
1092         }, function (error) {
1093           $mwtnLog.info({ component: '$mwtnCommons.unmount', message: JSON.stringify(error.data) });
1094           deferred.reject(error);
1095         });
1096         return deferred.promise;
1097       }
1098
1099       /**
1100        * A promise to unmount netconf devices from OpenDaylight Beryllium and higher. 
1101        * @param {string} nodeId - The mountpoint identifier which unmounted/disconnected from OpenDaylight.
1102        */
1103       service.unmount = function (nodeId) {
1104         var url = [service.base, service.url.unmount(nodeId)].join('');
1105         var request = {
1106           method: 'DELETE',
1107           url: url
1108         };
1109         var deferred = $q.defer();
1110         $http(request).then(function (success) {
1111           $mwtnLog.info({ component: COMPONENT, message: 'Mounting Point deleted: ' + nodeId });
1112           deferred.resolve(success.data);
1113         }, function (error) {
1114           // try the old way
1115           unmountDeprecated(nodeId).then(
1116             function (success) {
1117               $mwtnLog.info({ component: COMPONENT, message: 'Mounting Point deleted: ' + nodeId });
1118               deferred.resolve(success.data);
1119             },
1120             function (error) {
1121               $mwtnLog.info({ component: '$mwtnCommons.unmount', message: JSON.stringify(error.data) });
1122               deferred.reject(error);
1123             }
1124           );
1125         });
1126         return deferred.promise;
1127       };
1128
1129       service.getPacParts = function (spec) {
1130         var errorMsg = { info: 'No data received' };
1131         var deferred = $q.defer();
1132         switch (spec.pacId) {
1133           case 'ne':
1134             service.getActualNetworkElement(spec.nodeId, spec.revision).then(function (success) {
1135               deferred.resolve(success);
1136             }, function (error) {
1137               $mwtnLog.error({ component: COMPONENT, message: 'Requesting ' + spec.nodeId + ' failed!' });
1138               deferred.reject(error);
1139             });
1140             break;
1141           case 'clock':
1142             service.getPtpClockData(spec.nodeId, spec.revision).then(function (success) {
1143               deferred.resolve(success);
1144             }, function (error) {
1145               $mwtnLog.error({ component: COMPONENT, message: 'Requesting clock for ' + spec.nodeId + ' failed!' });
1146               deferred.reject();
1147             });
1148             break;
1149           case 'forwardingDomain':
1150             console.warn('fd', JSON.stringify(spec));
1151             service.getForwardingDomain(spec.nodeId, 'eth-switch').then(function (success) {
1152               deferred.resolve(success);
1153             }, function (error) {
1154               $mwtnLog.error({ component: COMPONENT, message: 'Requesting forwarding domain for ' + spec.nodeId + ' failed!' });
1155               deferred.reject(error);
1156             });
1157             break;
1158           case 'ltp':
1159             var ltpKey = 'ltp';
1160             switch (spec.revision) {
1161               case '2017-02-17':
1162               case '2017-03-20':
1163               case '2017-03-24':
1164               case '2017-10-20':
1165               ltpKey = 'ltp';
1166                 break;
1167               default:
1168                 ltpKey = '_ltpRefList';
1169             }
1170             var odlRequest = {
1171               method: 'GET',
1172               url: [service.url.actualNetworkElement(spec.nodeId, spec.revision), ltpKey, spec.layerProtocolId].join('/')
1173             };
1174             // console.info(odlRequest.url);
1175             service.genericRequest(odlRequest).then(function (success) {
1176               deferred.resolve(success);
1177             }, function (error) {
1178               $mwtnLog.error({ component: COMPONENT, message: 'Requesting LTPs of ' + spec.nodeId + ' failed!' });
1179               deferred.reject(errorMsg);
1180             });
1181             break;
1182           case 'MWPS':
1183           case 'MWS':
1184           case 'ETH-CTP':
1185           case 'ETC':
1186           case 'airinterface':
1187           case 'structure':
1188           case 'container':
1189           // 3rd PoC
1190           case 'MicrowaveModel-ObjectClasses-AirInterface:MW_AirInterface_Pac':
1191           case 'MicrowaveModel-ObjectClasses-EthernetContainer:MW_EthernetContainer_Pac':
1192           case 'MicrowaveModel-ObjectClasses-PureEthernetStructure:MW_PureEthernetStructure_Pac':
1193           // 4th PoC
1194           case 'microwave-model:mw-air-interface-pac':
1195           case 'microwave-model:mw-air-interface-diversity-pac':
1196           case 'microwave-model:mw-pure-ethernet-structure-pac':
1197           case 'microwave-model:mw-hybrid-mw-structure-pac':
1198           case 'microwave-model:mw-tdm-container-pac':
1199           case 'microwave-model:mw-ethernet-container-pac':
1200           case 'onf-ethernet-conditional-packages:ethernet-pac':
1201           // PoC 4.1
1202           case 'onf-otn-odu-conditional-packages:otn-odu-connection-pac':
1203           case 'onf-otn-odu-conditional-packages:otn-odu-termination-pac':
1204             if (spec.partId) {
1205               service.getConditionalPackagePart(spec).then(function (success) {
1206                 success.layerProtocol = spec.layerProtocolId;
1207                 deferred.resolve(success);
1208               }, function (error) {
1209                 $mwtnLog.error({ component: COMPONENT, message: 'Requesting conditional package of ' + JSON.stringify(spec) + ' failed!' });
1210                 deferred.reject(errorMsg);
1211               });
1212             } else {
1213               deferred.resolve();
1214             }
1215             break;
1216           case 'neCurrentProblems':
1217             service.getNetworkElementCurrentProblemList(spec.nodeId, spec.revision).then(function (success) {
1218               deferred.resolve(success);
1219             }, function (error) {
1220               $mwtnLog.error({ component: COMPONENT, message: 'Requesting ' + spec.nodeId + ' failed!' });
1221               deferred.reject(error);
1222             });
1223             break;
1224           case 'mountpoint':
1225           case 'forwardingConstructs':
1226             // not needed (currently)
1227             deferred.resolve();
1228             break;
1229           default:
1230             $mwtnLog.error({ component: COMPONENT, message: 'Requesting ' + spec.pacId + ' is not supported!' });
1231             deferred.reject(errorMsg);
1232         }
1233         return deferred.promise;
1234       };
1235       service.setPacParts = function (spec, data) {
1236         var errorMsg = { info: 'No data received' };
1237         var deferred = $q.defer();
1238         switch (spec.pacId) {
1239           //  case 'ne':
1240           //    service.getActualNetworkElement(spec.nodeId, spec.revision).then(function(success){
1241           //      deferred.resolve(success);
1242           //    }, function(error){
1243           //      $mwtnLog.error({component: COMPONENT, message: 'Requesting ' + spec.nodeId + ' failed!'});
1244           //      deferred.reject(errorMsg);
1245           //    });
1246           //    break;
1247           //  case 'ltp':
1248           //    var odlRequest = {
1249           //      method: 'GET',
1250           //      url: [service.url.actualNetworkElement(spec.nodeId, spec.revision), '_ltpRefList', spec.layerProtocolId].join('/')
1251           //    };
1252           //    service.genericRequest(odlRequest).then(function(success){
1253           //      deferred.resolve(success);
1254           //    }, function(error){
1255           //      $mwtnLog.error({component: COMPONENT, message: 'Requesting LTPs of ' + spec.nodeId + ' failed!'});
1256           //      deferred.reject(errorMsg);
1257           //    });
1258           //    break;
1259           case 'airinterface':
1260           case 'structure':
1261           case 'container':
1262           // 4th PoC
1263           case 'microwave-model:mw-air-interface-pac':
1264           case 'microwave-model:mw-air-interface-diversity-pac':
1265           case 'microwave-model:mw-pure-ethernet-structure-pac':
1266           case 'microwave-model:mw-hybrid-mw-structure-pac':
1267           case 'microwave-model:mw-tdm-container-pac':
1268           case 'microwave-model:mw-ethernet-container-pac':
1269           case 'onf-ethernet-conditional-packages:ethernet-pac':
1270           // PoC 4.1
1271           case 'onf-otn-odu-conditional-packages:otn-odu-connection-pac':
1272           case 'onf-otn-odu-conditional-packages:otn-odu-termination-pac':
1273               service.setConditionalPackagePart(spec, data).then(function (success) {
1274                 deferred.resolve(success);
1275               }, function (error) {
1276                 $mwtnLog.error({ component: COMPONENT, message: 'Modification of ' + JSON.stringify(spec) + ' failed!' });
1277                 deferred.reject(errorMsg);
1278               });
1279             break;
1280           default:
1281             $mwtnLog.error({ component: COMPONENT, message: 'Modification of ' + spec.pacId + ' not supported!' });
1282             deferred.reject(errorMsg);
1283         }
1284         return deferred.promise;
1285       };
1286       service.setPacPartLists = function (spec, listData) {
1287         var errorMsg = { info: 'No data received' };
1288         var deferred = $q.defer();
1289         switch (spec.pacId) {
1290           //  case 'ne':
1291           //    service.getActualNetworkElement(spec.nodeId, spec.revision).then(function(success){
1292           //      deferred.resolve(success);
1293           //    }, function(error){
1294           //      $mwtnLog.error({component: COMPONENT, message: 'Requesting ' + spec.nodeId + ' failed!'});
1295           //      deferred.reject(errorMsg);
1296           //    });
1297           //    break;
1298           //  case 'ltp':
1299           //    var odlRequest = {
1300           //      method: 'GET',
1301           //      url: [service.url.actualNetworkElement(spec.nodeId, spec.revision), '_ltpRefList', spec.layerProtocolId].join('/')
1302           //    };
1303           //    service.genericRequest(odlRequest).then(function(success){
1304           //      deferred.resolve(success);
1305           //    }, function(error){
1306           //      $mwtnLog.error({component: COMPONENT, message: 'Requesting LTPs of ' + spec.nodeId + ' failed!'});
1307           //      deferred.reject(errorMsg);
1308           //    });
1309           //    break;
1310           case 'airinterface':
1311           case 'structure':
1312           case 'container':
1313           // 4th PoC
1314           case 'microwave-model:mw-air-interface-pac':
1315           case 'microwave-model:mw-air-interface-diversity-pac':
1316           case 'microwave-model:mw-pure-ethernet-structure-pac':
1317           case 'microwave-model:mw-hybrid-mw-structure-pac':
1318           case 'microwave-model:mw-tdm-container-pac':
1319           case 'microwave-model:mw-ethernet-container-pac':
1320           case 'onf-ethernet-conditional-packages:ethernet-pac':
1321           // PoC 4.1
1322           case 'onf-otn-odu-conditional-packages:otn-odu-connection-pac':
1323           case 'onf-otn-odu-conditional-packages:otn-odu-termination-pac':
1324           service.setConditionalPackagePartList(spec, listData).then(function (success) {
1325                 deferred.resolve(success);
1326               }, function (error) {
1327                 $mwtnLog.error({ component: COMPONENT, message: 'Modification of ' + JSON.stringify(spec) + ' failed!' });
1328                 deferred.reject(errorMsg);
1329               });
1330             break;
1331           default:
1332             $mwtnLog.error({ component: COMPONENT, message: 'Modification of ' + spec.pacId + ' not supported!' });
1333             deferred.reject(errorMsg);
1334         }
1335         return deferred.promise;
1336       };
1337       var nodeIntId = 100;
1338       service.getNodeIntIdFromNodeId = function (nodeId) {
1339         nodeIntId = nodeIntId + 1;
1340         if (nodeId.contains('-')) {
1341           return nodeId.split('-')[1];
1342         }
1343         return nodeIntId;
1344       };
1345
1346       service.getRequiredNetworkElements = function (complete) {
1347         var sort = [{ _id: { order: 'asc' } }];;
1348         var query = {
1349           match: {
1350             required: true
1351           }
1352         };
1353         var deferred = $q.defer();
1354         $mwtnDatabase.getFilteredSortedData('mwtn', 'required-networkelement', 0, 10000, sort, query).then(
1355           function (success) {
1356             if (complete) {
1357               deferred.resolve(success.data.hits.hits);
1358             }
1359             var result = success.data.hits.hits.map(function (ne) {
1360               var yangifiedObj = service.yangifyObject(ne._source);
1361               var pacKey = 'microwave-model:mw-air-interface-pac';
1362               if (yangifiedObj['microwave-model-object-classes-air-interface:mw-air-interface-pac']) {
1363                 pacKey = 'microwave-model-object-classes-air-interface:mw-air-interface-pac';
1364               }
1365               var configKey = 'air-interface-configuration';
1366               var radioSignalIds = [];
1367               if (yangifiedObj[pacKey]) {
1368                 radioSignalIds = yangifiedObj[pacKey].filter(
1369                   function (mwps) {
1370                     return mwps[configKey] && mwps[configKey]['radio-signal-id'];
1371                   }
1372                 ).map(
1373                   function (mwps) {
1374                     return mwps[configKey]['radio-signal-id'];
1375                   }
1376                 ).sort();
1377               }
1378               return {
1379                 id: service.getNodeIntIdFromNodeId(yangifiedObj['node-id']),
1380                 name: yangifiedObj['node-id'],
1381                 ipaddress: yangifiedObj.connect.host,
1382                 port: yangifiedObj.connect.port,
1383                 username: yangifiedObj.connect.username,
1384                 password: yangifiedObj.connect.password,
1385                 radioSignalIds: JSON.stringify(radioSignalIds),
1386                 connectionStatus: 'disconnected'
1387               };
1388             });
1389             deferred.resolve(result);
1390           },
1391           function (error) {
1392             $mwtnLog.error({ component: COMPONENT, message: 'Problems in retrieving required network elements.' });
1393             deferred.reject(error);
1394           }
1395         );
1396         return deferred.promise;
1397       };
1398
1399       service.getConnectionStatus = function (neId) {
1400         var url = service.base + service.url.connectionStatus(neId);
1401         var request = {
1402           method: 'GET',
1403           url: url
1404         };
1405         var deferred = $q.defer();
1406         $http(request).then(function (success) {
1407           // console.log(JSON.stringify(success));
1408           deferred.resolve(success.data.node[0]['netconf-node-topology:connection-status']);
1409         }, function (error) {
1410           deferred.reject(error);
1411         });
1412         return deferred.promise;
1413       };
1414       service.executeClosedLoopAutomation = function () {
1415         var url = service.base + 'operations/closedLoopAutomation:start';
1416         var request = {
1417           method: 'POST',
1418           url: url
1419         };
1420         var deferred = $q.defer();
1421         $http(request).then(function (success) {
1422           deferred.resolve(success);
1423         }, function (error) {
1424           deferred.reject(error);
1425         });
1426         return deferred.promise;
1427       };
1428       service.saveClosedLoopAutomation = function (enabled, option) {
1429         var url = service.base + 'operations/closedLoopAutomation:save-timer';
1430         var request = {
1431           method: 'POST',
1432           url: url,
1433           data: {
1434             "input": {
1435               "enabled": enabled,
1436               "option": option
1437             }
1438           }
1439         };
1440         var deferred = $q.defer();
1441         $http(request).then(function (success) {
1442           deferred.resolve(success);
1443         }, function (error) {
1444           deferred.reject(error);
1445         });
1446         return deferred.promise;
1447       };
1448       service.readClosedLoopAutomation = function () {
1449         var url = service.base + 'operations/closedLoopAutomation:read-timer';
1450         var request = {
1451           method: 'POST',
1452           url: url,
1453         };
1454         var deferred = $q.defer();
1455         $http(request).then(function (success) {
1456           deferred.resolve(success);
1457         }, function (error) {
1458           deferred.reject(error);
1459         });
1460         return deferred.promise;
1461       };
1462
1463       /**
1464        * A promise inquiring the a single mountpoint from topology-netconf.
1465        * @param {string} nodeId - The mountpoint identifier.
1466        * @return {{'node-id':string, 'netconf-node-topology:tcp-only': boolean, 'netconf-node-topology:host', string, 'netconf-node-topology:keepalive-delay':number, 'netconf-node-topology:port':number, 'netconf-node-topology:username':string, 'netconf-node-topology:password': string}} - The mountpoint from topology-netconf.
1467        */
1468       service.getMountPoint = function (nodeId) {
1469         var odlRequest = {
1470           method: 'GET',
1471           url: service.url.mount(nodeId)
1472         };
1473         var deferred = $q.defer();
1474         service.genericRequest(odlRequest).then(
1475           function (success) {
1476             deferred.resolve(success.data.node[0]);
1477           },
1478           function (error) {
1479             deferred.reject(error);
1480           }
1481         );
1482         return deferred.promise;
1483       };
1484
1485       service.getMountPoints = function () {
1486         var url = service.base + service.url.actualNetworkElements();
1487         var request = {
1488           method: 'GET',
1489           url: url
1490         };
1491         var deferred = $q.defer();
1492         $http(request).then(function (success) {
1493           var requiredTopology = 'topology-netconf';
1494           var topo = success.data.topology.filter(function (topo) {
1495             return topo['topology-id'] === requiredTopology;
1496           });
1497           // console.log('topo', JSON.stringify(topo));
1498           if (topo.length === 0) {
1499             var message = ['ODL', requiredTopology, 'not found!'].join(' ');
1500             $mwtnLog.error({ component: COMPONENT, message: message });
1501             deferred.reject(message);
1502           } else if (topo[0].node) {
1503             var mwMountPoints = topo[0].node.filter(function (mountpoint) {
1504               return mountpoint['node-id'] !== 'controller-config';
1505             }).map(function (mountpoint) {
1506               // console.info('mountpoint', JSON.stringify(mountpoint));
1507               var capId = 'netconf-node-topology:available-capabilities';
1508               if (mountpoint[capId] && mountpoint[capId]['available-capability']) {
1509                 var caps = mountpoint[capId]['available-capability'].filter(function (cap) {
1510                   // console.info(JSON.stringify(cap));
1511                   return cap.capability.contains('?revision=');
1512                 }).map(function (cap) {
1513                   return {
1514                     module: cap.capability.split(')')[1],
1515                     revision: cap.capability.split('?revision=')[1].substring(0, 10)
1516                   };
1517                 }).sort(function (a, b) {
1518                   if (a.module < b.module) return -1;
1519                   if (a.module > b.module) return 1;
1520                   return 0;
1521                 });
1522                 // console.log('mountpoint', JSON.stringify(caps));
1523                 mountpoint.onfCapabilities = caps;
1524                 mountpoint.onfCoreModelRevision = caps.filter(function (cap) {
1525                   return cap.module === 'core-model' || cap.module === 'CoreModel-CoreNetworkModule-ObjectClasses';
1526                 }).map(function (cap) {
1527                   return cap.revision;
1528                 });
1529                 if (mountpoint.onfCoreModelRevision.length === 1) {
1530                   mountpoint.onfCoreModelRevision = mountpoint.onfCoreModelRevision[0];
1531                 } else {
1532                   $mwtnLog.error({ component: COMPONENT, message: mountpoint.onfCoreModelRevision.length + ' CoreModels supported by ' + mountpoint['node-id'] });
1533                 }
1534
1535                 // console.log('caps', JSON.stringify(caps));
1536                 mountpoint.onfAirInterfaceRevision = caps.filter(function (cap) {
1537                   return cap.module === 'microwave-model' || cap.module === 'MicrowaveModel-ObjectClasses-AirInterface';
1538                 }).map(function (cap) {
1539                   return cap.revision;
1540                 });
1541                 // console.log('onfAirInterfaceRevision', mountpoint.onfAirInterfaceRevision);
1542                 if (mountpoint.onfAirInterfaceRevision.length === 1) {
1543                   mountpoint.onfAirInterfaceRevision = mountpoint.onfAirInterfaceRevision[0];
1544                 } else {
1545                   $mwtnLog.error({ component: COMPONENT, message: 'More than 1 or no MicrowaveModel supported by ' + mountpoint['node-id'] });
1546                 }
1547               }
1548               var clusterConneactionStatus = 'netconf-node-topology:clustered-connection-status';
1549               if (mountpoint[clusterConneactionStatus] && mountpoint[clusterConneactionStatus]['netconf-master-node']) {
1550                 var value = mountpoint[clusterConneactionStatus]['netconf-master-node'];
1551                 value = value.substring(value.indexOf('@'));
1552                 mountpoint.client = value.substring(1, value.indexOf(':'));
1553               } else {
1554                 mountpoint.client = window.location.hostname;
1555               }
1556             return mountpoint;
1557             });
1558             // console.log('mwMountPoints', JSON.stringify(mwMountPoints));
1559             deferred.resolve(mwMountPoints);
1560           }
1561         }, function (error) {
1562           $mwtnLog.error({ component: COMPONENT, message: JSON.stringify(error.data) });
1563           deferred.reject(error);
1564         });
1565         return deferred.promise;
1566       };
1567       service.separator = '&nbsp;';
1568       // grid settings
1569       service.highlightFilteredHeader = function (row, rowRenderIndex,
1570         col, colRenderIndex) {
1571         if (col.filters[0].term) {
1572           return 'header-filtered';
1573         } else {
1574           return '';
1575         }
1576       };
1577       service.gridOptions = {
1578         data: [],
1579         enableColumnResizing: true,
1580         enableSorting: true,
1581         enableFiltering: true,
1582         enableGridMenu: true,
1583         exporterMenuPdf: false,
1584         showGridFooter: true,
1585         // showColumnFooter: true,
1586         fastWatch: true,
1587         enableRowSelection: true,
1588         enableRowHeaderSelection: true,
1589         multiSelect: false
1590       };
1591       service.gridOptions.gridMenuCustomItems = [{
1592         title: 'Rotate Grid',
1593         action: function ($event) {
1594           this.grid.element.toggleClass('rotated');
1595         },
1596         order: 210
1597       }];
1598       service.url = {
1599         actualNetworkElements: function () {
1600           return 'operational/network-topology:network-topology/topology/topology-netconf';
1601         },
1602         connectionStatus: function (neId) {
1603           return 'operational/network-topology:network-topology/topology/topology-netconf/node/' + neId;
1604         },
1605         mount: function (neId) {
1606           return 'config/network-topology:network-topology/topology/topology-netconf/node/' + neId;
1607           // return 'config/network-topology:network-topology/topology/topology-netconf/node/controller-config/yang-ext:mount/config:modules'; // depricated 
1608         },
1609         unmount: function (neId) {
1610           // return 'config/network-topology:network-topology/topology/topology-netconf/node/controller-config/yang-ext:mount/config:modules/module/odl-sal-netconf-connector-cfg:sal-netconf-connector/' + neId; // depricated 
1611           return 'config/network-topology:network-topology/topology/topology-netconf/node/' + neId;
1612         },
1613         forwardingDomain: function (neId, fdUuid) {
1614           return 'operational/network-topology:network-topology/topology/topology-netconf/node/' + neId + '/yang-ext:mount/core-model:network-element/fd/' +fdUuid;
1615         },
1616         forwardingConstruct: function (neId, fcUuid) {
1617           return 'operational/network-topology:network-topology/topology/topology-netconf/node/' + neId + '/yang-ext:mount/core-model:forwarding-construct/' +fcUuid;
1618         },
1619         clock: function (neId, revision) {
1620           return 'operational/network-topology:network-topology/topology/topology-netconf/node/' + neId + '/yang-ext:mount/ietf-ptp-dataset:instance-list/1';
1621         },
1622         actualNetworkElement: function (neId, revision) {
1623           switch (revision) {
1624             case "2016-03-23":
1625               return [
1626                 'operational/network-topology:network-topology/topology/topology-netconf/node/',
1627                 neId,
1628                 '/yang-ext:mount/CoreModel-CoreNetworkModule-ObjectClasses:NetworkElement/',
1629                 neId].join('');
1630             case "2017-02-17":
1631             case "2017-03-20":
1632             case "2017-03-24":
1633             case "2017-10-20":
1634             return [
1635                 'operational/network-topology:network-topology/topology/topology-netconf/node/',
1636                 neId,
1637                 '/yang-ext:mount/core-model:network-element'].join('');
1638             default: // 2016-08-11
1639               return [
1640                 'operational/network-topology:network-topology/topology/topology-netconf/node/',
1641                 neId,
1642                 '/yang-ext:mount/CoreModel-CoreNetworkModule-ObjectClasses:NetworkElement'].join('');
1643           }
1644         },
1645         networkElementCurrentProblemList: function (neId, revision) {
1646           console.log(neId, revision);
1647           switch (revision) {
1648             case "2016-08-11":
1649               return [
1650                 'operational/network-topology:network-topology/topology/topology-netconf/node/',
1651                 neId,
1652                 '/yang-ext:mount/MicrowaveModel-NetworkElement-CurrentProblemList:NetworkElementCurrentProblems'].join('');
1653             case "2017-02-17":
1654             case "2017-03-20": // TODO sko equipmentAlarms check new yang file if agreed
1655             return [
1656                 'operational/network-topology:network-topology/topology/topology-netconf/node/',
1657                 neId,
1658                 '/yang-ext:mount/onf-core-model-conditional-packages:network-element-pac/network-element-current-problems'].join('');
1659             default:
1660               return [
1661                 'operational/network-topology:network-topology/topology/topology-netconf/node/',
1662                 neId,
1663                 '/yang-ext:mount/onf-core-model-conditional-packages:network-element-pac/network-element-current-problems'].join('');
1664           }
1665         }
1666       };
1667       /*
1668        * Changes different time formats to a common time fromat
1669        * TODO currently not implemented!
1670        */
1671       service.normalizeTimeFormat = function (time, format) {
1672         return time;
1673       };
1674       /*
1675        * Changing a string according to yang naming conventions.
1676        * The function should be alinged with the ONF Eagle project.
1677        */
1678       service.yangify = function (str) {
1679         var result = str
1680           .replace(/RefList+$/, '')                // endling "List" was removed
1681           .replace(/List+$/, '')                   // endling "List" was removed
1682           .replace(/([a-z])([A-Z])/g, '$1-$2')    // insert dashes
1683           .replace(/([0-9])([a-zA-Z])/g, '$1-$2')       // insert dashes
1684           .replace(/([A-Z])([A-Z])([a-z])/g, '$1-$2$3') // insert dashes
1685           .toLowerCase()                            // lowercase everything
1686           .replace(/^_/, '')                       // remove leading underscore
1687           .replace(/:_/g, ':')                     // and leading underscores in path segments
1688           .replace(/_/g, '-');                     // convert underscore to dashes
1689
1690         // catch "wrong" UML labels
1691         var exceptions = {
1692           'air-interface':'air-interface-list',
1693           'air-interface-ltp':'air-interface-ltp-list',
1694           'air-interface-capability': 'air-interface-capability-list',
1695           'air-interface-current-problem': 'air-interface-current-problem-list',
1696           'container-capability': 'container-capability-list',
1697           'current-performance-data':'current-performance-data-list',
1698           'current-problem':'current-problem-list',
1699           'historical-performance-data':'historical-performance-data-list',
1700           'problem-kind-severity':'problem-kind-severity-list',
1701           'pure-ethernet-structure-capability':'pure-ethernet-structure-capability-list',
1702           'segment-status':'segment-status-list',
1703           'segments-id':'segments-id-list',
1704           'structure-capability': 'structure-capability-list',
1705           'structure-current-problem': 'structure-current-problem-list',
1706           'supported-tdm-container-types':'supported-tdm-container-types-list',
1707           'supported-tdm-structure-types':'supported-tdm-structure-types-list',
1708           'supported-channel-plan':'supported-channel-plan-list',
1709           'supported-loop-back-kind':'transmission-mode-list',
1710           'transmission-mode':'transmission-mode-list'
1711         };
1712         if (exceptions[result]) {
1713           console.warn(result, '.>', exceptions[result]);
1714           result=exceptions[result];
1715         }
1716
1717         // catch modulation value difference
1718         if (result.startsWith('time') && result.endsWith('symbols')
1719           || result.startsWith('time') && result.contains('-symbols-') && result.endsWith('-s')
1720           || result.startsWith('time') && result.contains('-symbols-') && result.endsWith('-l')) {
1721           result = result.replace('symbols', 'states');
1722         }
1723         return result;
1724       };
1725
1726       /*
1727        * Checking, whether a jsonObject should be yangifyed
1728        */
1729       service.isCamelCase = function (jsonObject) {
1730         if (jsonObject === undefined || jsonObject === null) {
1731           return true;
1732         }
1733         var result;
1734         var type = service.getType(jsonObject);
1735         switch (type) {
1736           case 'object':
1737             result = false;
1738             Object.keys(jsonObject).map(function (key) {
1739               result = result || key !== [service.yangify(key)];
1740             });
1741             break;
1742           case 'array':
1743           case 'boolean':
1744           case 'function':
1745           case 'string':
1746           case 'number':
1747           case 'null':
1748           case 'undefined':
1749             result = true;
1750             break;
1751           default:
1752             console.error('Type1:', type, ' is not supported!');
1753             result = true;
1754         }
1755         return result;
1756       };
1757       /*
1758        * Yangifies a names/keys of a jsonOject
1759        */
1760       service.yangifyObject = function (jsonObject) {
1761         if (jsonObject === undefined || jsonObject === null) {
1762           return jsonObject;
1763         }
1764         var result;
1765         var type = service.getType(jsonObject);
1766         switch (type) {
1767           case 'object':
1768           case 'name-value':
1769             result = {};
1770             Object.keys(jsonObject).map(function (key) {
1771               result[service.yangify(key)] = service.yangifyObject(jsonObject[key]);
1772             });
1773             break;
1774           case 'array':
1775             result = jsonObject.map(function (item, index) {
1776               return service.yangifyObject(item);
1777             });
1778             break;
1779           case 'boolean':
1780           case 'function':
1781           case 'string':
1782           case 'number':
1783           case 'null':
1784           case 'undefined':
1785             result = jsonObject;
1786             break;
1787           default:
1788             console.error('Type:', type, ' is not supported!');
1789             result = jsonObject;
1790         }
1791         return result;
1792       };
1793       /*
1794        * Send a restconf request to OpenDayligth.
1795        * All ODL restconf requests should pass this function.
1796        */
1797       service.genericRequest = function (odlRequest) {
1798         var url = [service.base, odlRequest.url].join('');
1799         var request = {
1800           method: odlRequest.method,
1801           url: url,
1802           data: odlRequest.data
1803         };
1804         var deferred = $q.defer();
1805         $http(request).then(function (success) {
1806           // yangifing the response is required until all NEs has switch to CoreModel 1.2 (ONF-TR-532)
1807           // deferred.resolve(service.yangifyObject(success));
1808           deferred.resolve(success);
1809         }, function (error) {
1810           $mwtnLog.error({ component: COMPONENT + '.genericRequest', message: JSON.stringify(error.data) });
1811           deferred.reject(error);
1812         });
1813         return deferred.promise;
1814       };
1815       service.getMountedNetConfServers = function (callback) {
1816         var url = service.base + service.url.actualNetworkElements();
1817         var request = {
1818           method: 'GET',
1819           url: url
1820         };
1821         $http(request).then(function (success) {
1822           return callback(success.data);
1823         }, function (error) {
1824           console.error(JSON.stringify(error));
1825           return callback();
1826         });
1827       };
1828
1829       service.getActualNetworkElement = function (neId, revision) {
1830         var url = [service.base,
1831         service.url.actualNetworkElement(neId, revision)].join('');
1832         var request = {
1833           method: 'GET',
1834           url: url
1835         };
1836         var taskId = [neId, 'ONF:CoreModel:NetworkElement data received'].join(' ');
1837
1838         var deferred = $q.defer();
1839         console.time(taskId);
1840         $http(request).then(function (success) {
1841           console.timeEnd(taskId);
1842           success.data.revision = revision;
1843           //  deferred.resolve(service.yangifyObject(success.data));
1844           deferred.resolve(success.data);
1845         }, function (error) {
1846           console.timeEnd(taskId);
1847           $mwtnLog.info({ component: '$mwtnCommons.getActualNetworkElement', message: JSON.stringify(error.data) });
1848           deferred.reject(error);
1849         });
1850         return deferred.promise;
1851       };
1852
1853       service.getForwardingDomain = function(neId, fdUuid) {
1854         var url = [service.base,
1855         service.url.forwardingDomain(neId, fdUuid)].join('');
1856         var request = {
1857           method: 'GET',
1858           url: url
1859         };
1860         var taskId = [neId, 'ONF:ForwardingDomain received'].join(' ');
1861
1862         var deferred = $q.defer();
1863         console.time(taskId);
1864         $http(request).then(function (success) {
1865           console.timeEnd(taskId);
1866           //  deferred.resolve(service.yangifyObject(success.data));
1867           deferred.resolve(success.data);
1868         }, function (error) {
1869           console.timeEnd(taskId);
1870           $mwtnLog.info({ component: '$mwtnCommons.getForwardingDomain', message: JSON.stringify(error.data) });
1871           deferred.reject(error);
1872         });
1873         return deferred.promise;
1874       };
1875
1876       service.getForwardingConstruct = function(neId, fcUuid) {
1877         var url = [service.base,
1878         service.url.forwardingConstruct(neId, fcUuid)].join('');
1879         var request = {
1880           method: 'GET',
1881           url: url
1882         };
1883         var taskId = [neId, fcUuid, 'ONF:ForwardingConstruct received'].join(' ');
1884
1885         var deferred = $q.defer();
1886         console.time(taskId);
1887         $http(request).then(function (success) {
1888           console.timeEnd(taskId);
1889           //  deferred.resolve(service.yangifyObject(success.data));
1890           deferred.resolve(success.data);
1891         }, function (error) {
1892           console.timeEnd(taskId);
1893           $mwtnLog.info({ component: '$mwtnCommons.getForwardingConstruct', message: JSON.stringify(error.data) });
1894           deferred.reject();
1895         });
1896         return deferred.promise;
1897       };
1898
1899       service.getPtpClockData = function(neId, revision) {
1900         var url = [service.base,
1901         service.url.clock(neId, revision)].join('');
1902         var request = {
1903           method: 'GET',
1904           url: url
1905         };
1906         var taskId = [neId, 'ONF:PTP:DataSet received'].join(' ');
1907
1908         var deferred = $q.defer();
1909         console.time(taskId);
1910         $http(request).then(function (success) {
1911           console.timeEnd(taskId);
1912           success.data.revision = revision;
1913           //  deferred.resolve(service.yangifyObject(success.data));
1914           deferred.resolve(success.data);
1915         }, function (error) {
1916           console.timeEnd(taskId);
1917           $mwtnLog.info({ component: '$mwtnCommons.getPtpClockData', message: JSON.stringify(error.data) });
1918           deferred.reject(error);
1919         });
1920         return deferred.promise;
1921       };
1922
1923       service.getNetworkElementCurrentProblemList = function (neId, revision) {
1924         var url = [service.base,
1925         service.url.networkElementCurrentProblemList(neId, revision)].join('');
1926         var request = {
1927           method: 'GET',
1928           url: url
1929         };
1930         var taskId = [neId, 'ONF:CoreModel:NetworkElement data received'].join(' ');
1931
1932         var deferred = $q.defer();
1933         console.time(taskId);
1934         $http(request).then(function (success) {
1935           console.timeEnd(taskId);
1936           success.data.revision = revision;
1937           // deferred.resolve(service.yangifyObject(success.data)); TODO: check if correct
1938           deferred.resolve(success.data);
1939         }, function (error) {
1940           console.timeEnd(taskId);
1941           $mwtnLog.info({ component: '$mwtnCommons.getActualNetworkElement', message: JSON.stringify(error.data) });
1942           deferred.reject(error);
1943         });
1944         return deferred.promise;
1945       };
1946
1947       var getIdsByRevision = function (revision, pacId, partId) {
1948
1949         switch (revision) {
1950           case '2016-03-23':
1951             switch (pacId) {
1952               case 'MWPS':
1953               case 'AirInterface':
1954               case 'airinterface':
1955               case 'airInterface':
1956                 pacId = 'MicrowaveModel-ObjectClasses-MwConnection:MW_AirInterface_Pac';
1957                 partId = 'airInterface' + partId;
1958                 if (partId === 'airInterfaceCapability' || partId === 'airInterfaceCurrentProblems') {
1959                   partId = undefined;
1960                 }
1961                 break;
1962               case 'MWS':
1963               case 'Structure':
1964               case 'structure':
1965                 pacId = 'MicrowaveModel-ObjectClasses-MwConnection:MW_Structure_Pac';
1966                 partId = 'structure' + partId;
1967                 break;
1968               case 'ETH-CTP':
1969               case 'ETH':
1970               case 'Container':
1971               case 'container':
1972                 pacId = 'MicrowaveModel-ObjectClasses-MwConnection:MW_Container_Pac';
1973                 partId = 'container' + partId;
1974                 break;
1975               case 'TDM':
1976                 pacId = 'MicrowaveModel-ObjectClasses-MwConnection:MW_Container_Pac';
1977                 partId = 'container' + partId;
1978                 break;
1979             }
1980             break;
1981           case '2017-02-17':
1982           case '2017-03-20':
1983           case '2017-03-24':
1984             switch (pacId) {
1985               case 'MWPS':
1986               case 'AirInterface':
1987               case 'airinterface':
1988               case 'airInterface':
1989               case 'air-interface':
1990                 pacId = 'microwave-model:mw-air-interface-pac';
1991                 partId = 'air-interface-' + service.yangify(partId);
1992                 break;
1993               case 'MWS':
1994               case 'Structure':
1995               case 'structure':
1996                 var isHybrid = false; // TODO How do I know this? 
1997                 if (isHybrid) {
1998                   pacId = 'microwave-model:mw-hybrid-mw-structure-pac';
1999                   partId = 'hybrid-mw-structure-' + service.yangify(partId);
2000                 } else {
2001                   pacId = 'microwave-model:mw-pure-ethernet-structure-pac';
2002                   partId = 'pure-ethernet-structure-' + service.yangify(partId);
2003                 }
2004                 break;
2005               case 'ETH-CTP':
2006               case 'ETC':
2007               case 'Container':
2008               case 'container':
2009                 pacId = 'microwave-model:mw-ethernet-container-pac';
2010                 partId = 'ethernet-container-' + service.yangify(partId);
2011                 break;
2012               case 'TDM':
2013                 pacId = 'microwave-model:mw-tdm-container-pac';
2014                 partId = 'tdm-container-' + service.yangify(partId);
2015                 break;
2016             }
2017             break;
2018           default:
2019             switch (pacId) {
2020               case 'MWPS':
2021               case 'AirInterface':
2022               case 'airinterface':
2023               case 'airInterface':
2024                 pacId = 'MicrowaveModel-ObjectClasses-AirInterface:MW_AirInterface_Pac';
2025                 partId = 'airInterface' + partId;
2026                 break;
2027               case 'MWS':
2028               case 'Structure':
2029               case 'structure':
2030                 pacId = 'MicrowaveModel-ObjectClasses-PureEthernetStructure:MW_PureEthernetStructure_Pac';
2031                 partId = 'pureEthernetStructure' + partId;
2032                 break;
2033               case 'ETH-CTP':
2034               case 'ETH':
2035               case 'Container':
2036               case 'container':
2037                 pacId = 'MicrowaveModel-ObjectClasses-EthernetContainer:MW_EthernetContainer_Pac';
2038                 partId = 'ethernetContainer' + partId;
2039                 break;
2040               case 'TDM':
2041                 pacId = 'microwave-model:mw-tdm-container-pac';
2042                 partId = 'tdm-container-' + service.yangify(partId);
2043                 break;
2044             }
2045         }
2046         return {
2047           pacId: pacId,
2048           partId: partId
2049         };
2050       };
2051
2052       service.getConditionalPackagePart = function (spec) {
2053         // console.log(JSON.stringify(spec));
2054         var deferred = $q.defer();
2055         if (!spec.partId) {
2056           deferred.reject('ignore');
2057           return deferred.promise;
2058         }
2059
2060         var ids = getIdsByRevision(spec.revision, spec.pacId, spec.partId);
2061         var operation = 'operational';
2062         if (spec.config === true) {
2063           operation = 'config';
2064         }
2065
2066         var url = [service.base, operation,
2067           '/network-topology:network-topology/topology/topology-netconf/node/',
2068         spec.nodeId,
2069           '/yang-ext:mount/', ids.pacId, '/',
2070         spec.layerProtocolId, '/',
2071         ids.partId].join('');
2072         var request = {
2073           method: 'GET',
2074           url: url
2075         };
2076         // console.log(JSON.stringify(request));
2077
2078         var taskId = [spec.nodeId, spec.layerProtocolId, spec.pacId, 'data received'].join(' ');
2079         console.time(taskId);
2080         $http(request).then(function (success) {
2081           console.timeEnd(taskId);
2082           success.data.revision = spec.revision;
2083           deferred.resolve(success.data);
2084           // console.log(JSON.stringify(service.yangifyObject(success.data)));
2085           // [sko] not now - later after all apps are updated to the new model: deferred.resolve(service.yangifyObject(success.data));
2086         }, function (error) {
2087           console.timeEnd(taskId);
2088           $mwtnLog.info({ component: '$mwtnCommons.getConditionalPackagePart', message: JSON.stringify(error.data) });
2089           deferred.reject(error);
2090         });
2091         return deferred.promise;
2092       };
2093
2094       service.getPtpPort = function (spec) {
2095         var deferred = $q.defer();
2096         if (!spec.networkElement || !spec.value) {
2097           deferred.reject('ignore');
2098           return deferred.promise;
2099         }
2100
2101         var operation = 'config';
2102         var url = [service.base, operation,
2103           '/network-topology:network-topology/topology/topology-netconf/node/',
2104         spec.networkElement,
2105           '/yang-ext:mount/ietf-ptp-dataset:instance-list/1/port-ds-list/',
2106         spec.value['port-number']].join('');
2107         var request = {
2108           method: 'GET',
2109           url: url
2110         };
2111         console.warn(JSON.stringify(request));
2112
2113         var taskId = [spec.networkElement, spec.value['port-number'], 'data received'].join(' ');
2114         console.time(taskId);
2115         $http(request).then(function (success) {
2116           console.timeEnd(taskId);
2117           console.warn(JSON.stringify(success.data));
2118           deferred.resolve(success.data);
2119         }, function (error) {
2120           console.timeEnd(taskId);
2121           $mwtnLog.info({ component: '$mwtnCommons.getPtpPort', message: JSON.stringify(error.data) });
2122           deferred.reject(error);
2123         });
2124         return deferred.promise;
2125       };
2126
2127       service.setPtpPort = function (spec, data) {
2128         var deferred = $q.defer();
2129         if (!spec.networkElement || !spec.value) {
2130           deferred.reject('ignore');
2131           return deferred.promise;
2132         }
2133
2134         var operation = 'config';
2135         var url = [service.base, operation,
2136           '/network-topology:network-topology/topology/topology-netconf/node/',
2137         spec.networkElement,
2138           '/yang-ext:mount/ietf-ptp-dataset:instance-list/1/port-ds-list/',
2139         spec.value['port-number']].join('');
2140
2141         var body = {'port-ds-list': data};
2142
2143         var request = {
2144           method: 'PUT',
2145           url: url,
2146           headers: {
2147             'Content-Type': 'application/json',
2148             'Accept': 'application/json'
2149           },
2150           data: body
2151         };
2152
2153         var taskId = [spec.networkElement, spec.value['port-number'], 'data received'].join(' ');
2154         console.time(taskId);
2155         $http(request).then(function (success) {
2156           console.timeEnd(taskId);
2157           deferred.resolve(success.data);
2158           // deferred.resolve(service.yangifyObject(success.data));
2159         }, function (error) {
2160           console.timeEnd(taskId);
2161           $mwtnLog.error({ component: '$mwtnCommons.setPtpPort', message: JSON.stringify(error.data) });
2162           deferred.reject(error);
2163         });
2164         return deferred.promise;
2165       };
2166
2167
2168       service.getPtpDefaultDs = function (spec) {
2169         var deferred = $q.defer();
2170         if (!spec.networkElement || !spec.value) {
2171           deferred.reject('ignore');
2172           return deferred.promise;
2173         }
2174
2175         var operation = 'config';
2176         var url = [service.base, operation,
2177           '/network-topology:network-topology/topology/topology-netconf/node/',
2178         spec.networkElement,
2179           '/yang-ext:mount/ietf-ptp-dataset:instance-list/1/default-ds/'].join('');
2180         var request = {
2181           method: 'GET',
2182           url: url
2183         };
2184         console.warn(JSON.stringify(request));
2185
2186         var taskId = [spec.networkElement, 'default-ds', 'data received'].join(' ');
2187         console.time(taskId);
2188         $http(request).then(function (success) {
2189           console.timeEnd(taskId);
2190           console.warn(JSON.stringify(success.data));
2191           deferred.resolve(success.data);
2192         }, function (error) {
2193           console.timeEnd(taskId);
2194           $mwtnLog.info({ component: '$mwtnCommons.getPtpPort', message: JSON.stringify(error.data) });
2195           deferred.reject(error);
2196         });
2197         return deferred.promise;
2198       };
2199
2200       service.setPtpDefaultDs = function (spec, data) {
2201         var deferred = $q.defer();
2202         if (!spec.networkElement || !spec.value) {
2203           deferred.reject('ignore');
2204           return deferred.promise;
2205         }
2206
2207         var operation = 'config';
2208         var url = [service.base, operation,
2209           '/network-topology:network-topology/topology/topology-netconf/node/',
2210         spec.networkElement,
2211           '/yang-ext:mount/ietf-ptp-dataset:instance-list/1/default-ds/'].join('');
2212
2213         var body = {'default-ds': data};
2214
2215         var request = {
2216           method: 'PUT',
2217           url: url,
2218           headers: {
2219             'Content-Type': 'application/json',
2220             'Accept': 'application/json'
2221           },
2222           data: body
2223         };
2224
2225         var taskId = [spec.networkElement, 'default-ds', 'data received'].join(' ');
2226         console.time(taskId);
2227         $http(request).then(function (success) {
2228           console.timeEnd(taskId);
2229           deferred.resolve(success.data);
2230           // deferred.resolve(service.yangifyObject(success.data));
2231         }, function (error) {
2232           console.timeEnd(taskId);
2233           $mwtnLog.error({ component: '$mwtnCommons.setPtpPort', message: JSON.stringify(error.data) });
2234           deferred.reject(error);
2235         });
2236         return deferred.promise;
2237       };
2238
2239
2240       service.setConditionalPackagePart = function (spec, data) {
2241         var deferred = $q.defer();
2242         if (!spec.partId) {
2243           deferred.reject('ignore');
2244           return deferred.promise;
2245         }
2246
2247         var ids = getIdsByRevision(spec.revision, spec.pacId, spec.partId);
2248         var body = {};
2249         body[ids.partId] = data;
2250
2251         var url = [service.base,
2252           'config/network-topology:network-topology/topology/topology-netconf/node/',
2253         spec.nodeId,
2254           '/yang-ext:mount/', ids.pacId, '/',
2255         spec.layerProtocolId, '/',
2256         ids.partId].join('');
2257         var request = {
2258           method: 'PUT',
2259           url: url,
2260           headers: {
2261             'Content-Type': 'application/json',
2262             'Accept': 'application/json'
2263           },
2264           data: body
2265         };
2266
2267         var taskId = [spec.nodeId, spec.layerProtocolId, spec.pacId, 'data received'].join(' ');
2268         console.time(taskId);
2269         $http(request).then(function (success) {
2270           console.timeEnd(taskId);
2271           success.data.revision = spec.revision;
2272           deferred.resolve(success.data);
2273           // deferred.resolve(service.yangifyObject(success.data));
2274         }, function (error) {
2275           console.timeEnd(taskId);
2276           $mwtnLog.error({ component: '$mwtnCommons.setConditionalPackagePart', message: JSON.stringify(error.data) });
2277           deferred.reject(error);
2278         });
2279         return deferred.promise;
2280       };
2281
2282       // pureEthernetStructureConfiguration/problemKindSeverityList/value1
2283       // {
2284       //   "problemKindSeverityList": [
2285       //     {
2286       //       "problemKindName": "severity1",
2287       //       "problemKindSeverity": "warning"
2288       //     }
2289       //   ]
2290       // }
2291
2292       var processData = function (item, i, callback) {
2293         var spec = item.spec;
2294         var ids = getIdsByRevision(spec.revision, spec.pacId,
2295           spec.partId);
2296         item.spec = undefined;
2297         var body = {};
2298         body[spec.attribute] = [item];
2299         $mwtnDatabase.getSchema().then(function (schema) {
2300
2301           var key;
2302           Object.keys(item).map(function (k) {
2303             // works currently only for single key lists
2304             if (schema[k] && schema[k]['is-key']) {
2305               key = k;
2306             }
2307           });
2308
2309           var url = [
2310             service.base.slice(0, -1),
2311             'config/network-topology:network-topology/topology/topology-netconf/node',
2312             spec.nodeId, 'yang-ext:mount', ids.pacId,
2313             spec.layerProtocolId, ids.partId,
2314             spec.attribute, item[key]].join('/');
2315           var request = {
2316             method: 'PUT',
2317             url: url,
2318             headers: {
2319               'Content-Type': 'application/json',
2320               'Accept': 'application/json'
2321             },
2322             data: body
2323           };
2324           // console.log(JSON.stringify(request));
2325           var taskId = [spec.nodeId, spec.layerProtocolId,
2326           spec.pacId, item.problemKindName,
2327             'data received'].join(' ');
2328           console.time(taskId);
2329           $http(request).then(function (success) {
2330             console.timeEnd(taskId);
2331             success.data.revision = spec.revision;
2332             return callback();
2333           },
2334             function (error) {
2335               console.timeEnd(taskId);
2336               $mwtnLog
2337                 .error({
2338                   component: '$mwtnCommons.setConditionalPackagePart',
2339                   message: JSON
2340                     .stringify(error.data)
2341                 });
2342               return callback();
2343             });
2344         });
2345       };
2346
2347       service.setConditionalPackagePartList = function (spec, data) {
2348         var deferred = $q.defer();
2349         if (!spec.partId) {
2350           deferred.reject('ignore');
2351           return deferred.promise;
2352         }
2353
2354         data.map(function (item) {
2355           item.spec = spec;
2356         });
2357
2358         doSynchronousLoop(data, processData, function () {
2359           deferred.resolve();
2360         });
2361         return deferred.promise;
2362       };
2363
2364       var saveRequiredNetworkElement = function (requiredNode) {
2365         var url = [$mwtnDatabase.base, $mwtnDatabase.index, 'required-networkelement',
2366         requiredNode.nodeId].join('/');
2367         var bodyString = JSON.stringify(requiredNode);
2368         var headers = {
2369           'Content-Type': 'application/json',
2370           'Content-Length': bodyString.length
2371         };
2372         var request = {
2373           method: 'PUT',
2374           url: url,
2375           data: requiredNode
2376         };
2377
2378         var deferred = $q.defer();
2379         console.time('database:' + url);
2380         $http(request).then(function (success) {
2381           console.timeEnd('database:' + url);
2382           // console.log(JSON.stringify(success));
2383           deferred.resolve(success);
2384         }, function (error) {
2385           console.timeEnd('database:' + url);
2386           $mwtnLog.error({ component: '$mwtnCommons.saveRequiredNetworkElement', message: JSON.stringify(error.data) });
2387           deferred.reject(error);
2388         });
2389         return deferred.promise;
2390       };
2391
2392       /**
2393        * A function which inquires data from a netconf server and stores it in a database.
2394        * @param {{'node-id': string, ipAddress: string, port: number, username: string, password: string, site:string, onfCoreModelRevision: string, onfAirInterfaceRevision: string}} netconfServer - A netConf server object with all connectivity parameters.
2395        */
2396       service.addRequiredNetworkElement = function (netconfServer) {
2397         /** {Object} requiredNode - Data set to be stored in database */
2398         var requiredNode = {
2399           nodeId: netconfServer['node-id'],
2400           siteRef: netconfServer.site,
2401           onfCoreModelRevision: netconfServer.onfCoreModelRevision,
2402           onfAirInterfaceRevision: netconfServer.onfAirInterfaceRevision,
2403           required: true,
2404           connect: {
2405             mountId: netconfServer['node-id'],
2406             host: netconfServer['netconf-node-topology:host'],
2407             port: netconfServer['netconf-node-topology:port'],
2408             username: netconfServer.username,
2409             password: netconfServer.password
2410           },
2411           'core-model:network-element': {},
2412         };
2413
2414         var deferred = $q.defer();
2415         saveRequiredNetworkElement(requiredNode).then(function (success) {
2416           deferred.resolve(success);
2417         }, function (error) {
2418           $mwtnLog.error({ component: '$mwtnCommons.saveRequiredNetworkElement', message: JSON.stringify(error.data) });
2419           deferred.reject(error);
2420         });
2421
2422         // [sko] much simplified du to ONAP concepts,  no device configuration needs to be stored in database.
2423
2424         // // get NetworkElement object from node
2425         // var spec = {
2426         //   nodeId: requiredNode.nodeId,
2427         //   revision: requiredNode.onfCoreModelRevision,
2428         //   pacId: 'ne'
2429         // };
2430
2431         // var updatePart = function (spec, data) {
2432         //   data.layerProtocol = spec.layerProtocolId;
2433         //   requiredNode[spec.pacId].push(data);
2434         // };
2435
2436         // var numberOfLtps = -1;
2437         // var processLTPs = function (item, i, callback) {
2438         //   var ltp = new LogicalTerminationPoint(item);
2439         //   ltp.getLayerProtocols().map(
2440         //     /**
2441         //      * A function processing a layer-protocol object
2442         //      * @param {LayerProtocol} lp A layer-protocol object
2443         //      */
2444         //     function (lp) {
2445         //       var conditionalPackage = lp.getConditionalPackage(true);
2446         //       if (conditionalPackage !== '') {
2447         //         if (requiredNode[conditionalPackage] === undefined) {
2448         //           // create missing pac array
2449         //           requiredNode[conditionalPackage] = [];
2450         //         }
2451         //         var spec = {
2452         //           nodeId: requiredNode.nodeId,
2453         //           revision: requiredNode.onfCoreModelRevision,
2454         //           pacId: conditionalPackage,
2455         //           layer: lp.getLayer(),
2456         //           layerProtocolId: lp.getId()
2457         //         };
2458         //         spec.partId = service.getPartGlobalId(spec, 'configuration');
2459         //         // console.info(JSON.stringify(spec));
2460         //         service.getPacParts(spec).then(function (success) {
2461         //           // console.log(JSON.stringify(success));
2462         //           spec.message = ['Process LTP', i+1, 'of', numberOfLtps].join(' ');
2463         //           $notifying.notify(spec);
2464         //           updatePart(spec, service.yangifyObject(success));
2465         //           return callback();
2466         //         }, function (error) {
2467         //           spec.message = ['Process LTP', i+1, 'of', numberOfLtps].join(' ');
2468         //           $notifying.notify(spec);
2469         //           $mwtnLog.error({ component: '$mwtnCommons.processLTPs bad data', message: JSON.stringify(error) });
2470         //           return callback();
2471         //         });
2472         //       } else {
2473         //         $mwtnLog.info({ component: COMPONENT, message: 'No condtional package found: ' + ltp.getId() });
2474         //         return callback();
2475         //       }
2476         //     });
2477
2478         //   // console.log(JSON.stringify(ltp.getData()));
2479         // };
2480
2481         // service.getPacParts(spec).then(function (success) {
2482         //   success = service.yangifyObject(success);
2483         //   requiredNode['core-model:network-element'] = success['network-element'];
2484         //   var id = success['network-element']['node-id'];
2485         //   numberOfLtps = success['network-element'].ltp.length; 
2486         //   doSynchronousLoop(success['network-element'].ltp, processLTPs, function () {
2487         //     saveRequiredNetworkElement(requiredNode).then(function (success) {
2488         //       $notifying.notify( { nodeId: id, message: 'finish'} );
2489         //       deferred.resolve(success);
2490         //     }, function (error) {
2491         //       $mwtnLog.error({ component: '$mwtnCommons.saveRequiredNetworkElement', message: JSON.stringify(error.data) });
2492         //       deferred.reject(error);
2493         //     });
2494         //   });
2495
2496         // }, function (error) {
2497         //   $mwtnLog.error({ component: '$mwtnCommons.getPacParts', message: JSON.stringify(error.data) });
2498         //   deferred.reject(error);
2499         // });
2500         return deferred.promise;
2501       };
2502
2503       service.registerForOdlEvents = function (path, callback) {
2504         var request = {
2505           method: 'POST',
2506           url: [service.base,
2507             'operations/sal-remote:create-data-change-event-subscription']
2508             .join(''),
2509           data: {
2510             "input": {
2511               "path": path,
2512               "sal-remote-augment:datastore": "CONFIGURATION",
2513               "sal-remote-augment:scope": "SUBTREE"
2514             }
2515           }
2516         };
2517         $http(request).then(
2518           function successCallback(response) {
2519             // this callback will be called asynchronously
2520             // when the response is available
2521             // console.log(JSON.stringify(response));
2522             createStream(response.data.output['stream-name'],
2523               function (socketLocation) {
2524                 callback(socketLocation);
2525               });
2526           }, function errorCallback(response) {
2527             // called asynchronously if an error occurs
2528             // or server returns response with an error status.
2529             console.error(JSON.stringify(response));
2530           });
2531       };
2532
2533       return service;
2534     });
2535
2536     // Precision Time Protocol PTP
2537     mwtnCommonsApp.register.factory('$mwtnPtp', function ($http, $q, $mwtnCommons, PtpClock) {
2538       var key1 = 'netconf-node-topology:available-capabilities';
2539       var key2 = 'available-capability';
2540       var filterActivePtpClocks = function(mountpoints) {
2541         return mountpoints.filter(function(mountpoint) {
2542           if (!mountpoint) return false;
2543           if (!mountpoint[key1]) return false;
2544           if (!mountpoint[key1][key2]) return false;
2545           if (mountpoint['netconf-node-topology:connection-status'] !== 'connected') return false;
2546           var ptpCapability = mountpoint[key1][key2].filter(function(capability){
2547             return capability.contains('ietf-ptp-dataset');
2548           });
2549           return ptpCapability.length > 0;
2550         }).map(function(mountpoint){
2551           return mountpoint['node-id'];
2552         });
2553       };
2554       var ptpClocks = {};
2555       var processData = function (nodeId, i, callback) {
2556         $mwtnCommons.getPtpClockData(nodeId).then(
2557           function (success) {
2558             ptpClocks[nodeId] = new PtpClock(success);
2559             callback();
2560           },
2561           function (error) {
2562             console.error(error);
2563             callback();
2564           }
2565         );
2566       };
2567
2568       var service = {};
2569       service.getPtpClocks = function() {
2570         var deferred = $q.defer();
2571
2572         $mwtnCommons.getMountPoints().then(function (mountpoints) {
2573           var ptpClockNodeIds = filterActivePtpClocks(mountpoints);
2574           doSynchronousLoop(ptpClockNodeIds, processData, function () {
2575             deferred.resolve(ptpClocks);
2576           });
2577         }, function (error) {
2578           console.log(error);
2579           deferred.reject();
2580         });
2581
2582         return deferred.promise;
2583       };
2584
2585       service.getParent = function(nodeId) {
2586         var deferred = $q.defer();
2587         var request = {
2588           method: 'GET',
2589           url: 'operational/network-topology:network-topology/topology/topology-netconf/node/' + nodeId + '/yang-ext:mount/ietf-ptp-dataset:instance-list/1/parent-ds/parent-port-identity'
2590         };
2591
2592         $mwtnCommons.genericRequest(request).then(function(success){
2593           console.warn(JSON.stringify(success));
2594           deferred.resolve(success.data['parent-port-identity']);
2595         }, function(error) {
2596           console.error(JSon.stringify(error));
2597           deferred.reject();
2598         });
2599         return deferred.promise;
2600       };
2601
2602       return service;
2603     });
2604
2605     // Service log
2606     mwtnCommonsApp.register.factory('$mwtnLog', function ($http, $q, $mwtnDatabase) {
2607       var writeLogToDB = function (data, callback) {
2608         var url = [$mwtnDatabase.base, $mwtnDatabase.index, 'log'].join('/');
2609         var request = {
2610           method: 'POST',
2611           url: url,
2612           data: {
2613             timestamp: data.timestamp ? data.timestamp : new Date().toISOString(),
2614             type: data.type ? data.type : 'info',
2615             component: data.component ? data.component : 'unkonwn',
2616             message: data.message
2617           }
2618         };
2619         $http(request).then(function successCallback(response) {
2620           return callback(true);
2621         }, function errorCallback(response) {
2622           console.error(JSON.stringify(response));
2623           return callback(false);
2624         });
2625       };
2626
2627       var createIndex = function (index, callback) {
2628         var url = [$mwtnDatabase.base, index].join('/');
2629         var request = {
2630           method: 'POST',
2631           url: url,
2632           data: {
2633             timestamp: new Date().toISOString(),
2634             type: 'info',
2635             component: '$mwtnLog',
2636             message: 'init log'
2637           }
2638         };
2639         $http(request).then(function (success) {
2640           return callback(true);
2641         }, function (error) {
2642           console.error(JSON.stringify(error));
2643           return callback(false);
2644         });
2645       };
2646       var checkIndex = function (index, callback) {
2647         var url = [$mwtnDatabase.base, index].join('/');
2648         var request = {
2649           method: 'HEAD',
2650           url: url
2651         };
2652         $http(request).then(function successCallback(response) {
2653           return callback(true);
2654         }, function errorCallback(response) {
2655           console.error(JSON.stringify(response));
2656           createIndex(index, function (created) {
2657             return callback(created);
2658           });
2659         });
2660       };
2661       var checkDatabase = function (callback) {
2662         var url = $mwtnDatabase.base;
2663         var request = {
2664           method: 'GET',
2665           url: url
2666         };
2667         $http(request).then(function successCallback(response) {
2668           checkIndex($mwtnDatabase.index, function (exists) {
2669             return callback(exists);
2670           });
2671         }, function errorCallback(response) {
2672           console.error(JSON.stringify(response));
2673           return callback(false);
2674         });
2675       };
2676       var getData = function (type, log) {
2677         var data = {};
2678         data.timestamp = new Date().toISOString();
2679         switch (typeof log) {
2680           case 'string':
2681             data.type = type;
2682             data.component = 'unknown';
2683             data.message = log;
2684             break;
2685           case 'object':
2686             data.type = type;
2687             data.component = log.component;
2688             data.message = log.message;
2689             break;
2690           default:
2691             data.type = 'error';
2692             data.component = '$mwtnLog';
2693             data.message = 'pnf log service is called with wrong parameters.';
2694         }
2695         // console.log(JSON.stringify(data));
2696         return data;
2697       };
2698       var service = {
2699         base: $mwtnDatabase.base
2700       };
2701       service.debug = function (log) {
2702         var data = getData('debug', log);
2703         checkDatabase(function (isRunning) {
2704           if (isRunning) {
2705             writeLogToDB(data, function () {
2706               // console.log('log stored');
2707             });
2708           } else {
2709             console.error(data.timestamp, service.base,
2710               'Database (ElasticSerach) not reachable!?');
2711           }
2712         });
2713         console.log(data.timestamp, JSON.stringify(log));
2714       };
2715       service.error = function (log) {
2716         var data = getData('error', log);
2717         checkDatabase(function (isRunning) {
2718           if (isRunning) {
2719             writeLogToDB(data, function () {
2720               // console.log('log stored');
2721             });
2722           } else {
2723             console.error(data.timestamp, service.base,
2724               'Database (ElasticSerach) not reachable!?');
2725           }
2726         });
2727         console.error(data.timestamp, JSON.stringify(log));
2728       };
2729       service.info = function (log) {
2730         var data = getData('info', log);
2731         checkDatabase(function (isRunning) {
2732           if (isRunning) {
2733             writeLogToDB(data, function () {
2734               // console.log('log stored');
2735             });
2736           } else {
2737             console.error(data.timestamp, service.base, 'Database (ElasticSerach) not reachable!?');
2738           }
2739         });
2740         console.info(data.timestamp, JSON.stringify(log));
2741       };
2742       service.warning = function (log) {
2743         var data = getData('warning', log);
2744         checkDatabase(function (isRunning) {
2745           if (isRunning) {
2746             writeLogToDB(data, function () {
2747               // console.log('log stored');
2748             });
2749           } else {
2750             console.error(data.timestamp, service.base, 'Database (ElasticSerach) not reachable!?');
2751           }
2752         });
2753         console.warn(data.timestamp, JSON.stringify(log));
2754       };
2755       return service;
2756     });
2757
2758     // Service log
2759     mwtnCommonsApp.register.factory('$mwtnEthernet', function ($http, $q) {
2760       var service = {};
2761
2762       service.base = window.location.origin + '/restconf';
2763       service.url = {
2764         create: service.base + '/operations/route:create',
2765         delete: service.base + '/operations/route:delete'
2766       };
2767
2768       service.createForwardingConstruct = function(spec) {
2769         var deferred = $q.defer();
2770
2771         var request = {
2772           method: 'POST',
2773           url: service.url.create,
2774           data: {
2775                   input: {
2776                       vlanid: spec.vlan,
2777                       fc: [{
2778                               nodeName: spec.nodeId,
2779                               aEnd: spec.ltp1,
2780                               zEnd: spec.ltp2
2781                           }
2782                       ]
2783                   }
2784           }
2785         };
2786         console.log(JSON.stringify(spec));
2787         console.log(JSON.stringify(request));
2788
2789         var consoleId = 'create-forwarding-consturuct: ';
2790         console.time(consoleId);
2791         $http(request).then(function (success) {
2792           console.timeEnd(consoleId);
2793           console.log(JSON.stringify(success));
2794           deferred.resolve(success.data.output.status);
2795         }, function (error) {
2796           console.timeEnd(consoleId);
2797           // console.error(JSON.stringify(error.statusText));
2798           deferred.reject(error.statusText);
2799         });
2800         return deferred.promise;
2801       };
2802       
2803       service.deleteForwardingConstruct = function(spec) {
2804       
2805         var deferred = $q.defer();
2806         var request = {
2807           method: 'POST',
2808           url: service.url.delete,
2809           data: {input:{vlanid:spec.ltp.slice(-2)}}
2810         };
2811         console.log(JSON.stringify(spec));
2812         console.log(JSON.stringify(request));
2813
2814         var consoleId = 'delete-forwarding-consturuct: ';
2815         console.time(consoleId);
2816         $http(request).then(function (success) {
2817           console.timeEnd(consoleId);
2818           console.log(JSON.stringify(success));
2819           deferred.resolve(success.data.output.status);
2820         }, function (error) {
2821           console.timeEnd(consoleId);
2822           // console.error(JSON.stringify(error.statusText));
2823           deferred.reject(error.statusText);
2824         });
2825         return deferred.promise;
2826       };
2827       
2828       return service;
2829     });
2830
2831     // Service Database (ElasticSerach)
2832     mwtnCommonsApp.register.factory('$mwtnDatabase', function ($http, $q) {
2833       var service = {
2834         base: window.location.origin + '/database',
2835         index: 'mwtn',
2836         command: '_search',
2837         mwtn: 'todo'
2838       };
2839
2840       // TODO getBase is not needed anymore
2841       service.getBase = function (functionId) {
2842         var deferred = $q.defer();
2843         
2844         deferred.resolve({
2845           base: service.base,
2846           index: functionId
2847         });
2848
2849         return deferred.promise;
2850       };
2851
2852       service.genericRequest = function (databaseRequest) {
2853         var url = [databaseRequest.base, databaseRequest.index, databaseRequest.docType,
2854         databaseRequest.command].join('/');
2855         var request = {
2856           method: databaseRequest.method,
2857           url: url,
2858           data: {
2859             from: databaseRequest.from,
2860             size: databaseRequest.size,
2861             sort: databaseRequest.sort,
2862             filter: databaseRequest.filter,
2863             query: databaseRequest.query
2864           }
2865         };
2866         // overwrite request data if given
2867         if (databaseRequest.data) {
2868           request.data = databaseRequest.data;
2869         }
2870         var deferred = $q.defer();
2871         var consoleId = 'database: ' + url;
2872         console.time(consoleId);
2873         $http(request).then(function (success) {
2874           console.timeEnd(consoleId);
2875           // console.log(JSON.stringify(success));
2876           deferred.resolve(success);
2877         }, function (error) {
2878           console.timeEnd(consoleId);
2879           console.warn(JSON.stringify(error));
2880           deferred.reject(error);
2881         });
2882         return deferred.promise;
2883       };
2884
2885       service.aggregationRequest = function (databaseRequest) {
2886         var url = [databaseRequest.base, databaseRequest.index, databaseRequest.docType,
2887         databaseRequest.command].join('/');
2888         var request = {
2889           method: databaseRequest.method,
2890           url: url,
2891           data: databaseRequest.aggregation
2892         };
2893         var deferred = $q.defer();
2894         var consoleId = 'database: ' + url;
2895         console.time(consoleId);
2896         $http(request).then(function (success) {
2897           console.timeEnd(consoleId);
2898           deferred.resolve(success);
2899         }, function (error) {
2900           console.timeEnd(consoleId);
2901           console.warn(JSON.stringify(error));
2902           deferred.reject(error);
2903         });
2904         return deferred.promise;
2905       };
2906
2907       service.getAllData = function (functionId, docType, from, size, sort) {
2908         var deferred = $q.defer();
2909         service.getBase(functionId).then(function (success) {
2910           var databaseRequest = {
2911             method: 'POST',
2912             base: success.base,
2913             index: success.index,
2914             command: '_search',
2915             docType: docType,
2916             from: from,
2917             size: size,
2918             sort: sort,
2919             query: {
2920               match_all: {}
2921             }
2922           };
2923           service.genericRequest(databaseRequest).then(function (success) {
2924             // console.log('getAllData', success);
2925             deferred.resolve(success);
2926           }, function (error) {
2927             deferred.reject(error);
2928           });
2929         }, function (error) {
2930           deferred.reject(error);
2931         });
2932         return deferred.promise;
2933       };
2934
2935       service.getFilteredData = function (functionId, docType, from, size, query) {
2936         var deferred = $q.defer();
2937         service.getBase(functionId).then(function (success) {
2938           var databaseRequest = {
2939             method: 'POST',
2940             base: success.base,
2941             index: success.index,
2942             command: '_search',
2943             docType: docType,
2944             from: from,
2945             size: size,
2946             sort: [],
2947             query: query
2948           };
2949           service.genericRequest(databaseRequest).then(function (success) {
2950             // console.log('getAllData', success);
2951             deferred.resolve(success);
2952           }, function (error) {
2953             deferred.reject(error);
2954           });
2955         }, function (error) {
2956           deferred.reject(error);
2957         });
2958         return deferred.promise;
2959       };
2960
2961       service.getFilteredSortedData = function (functionId, docType, from, size, sort, query) {
2962         var deferred = $q.defer();
2963         service.getBase(functionId).then(function (success) {
2964           var databaseRequest = {
2965             method: 'POST',
2966             base: success.base,
2967             index: success.index,
2968             command: '_search',
2969             docType: docType,
2970             from: from,
2971             size: size,
2972             sort: sort,
2973             query: query
2974           };
2975           service.genericRequest(databaseRequest).then(function (success) {
2976             // console.log('getAllData', success);
2977             deferred.resolve(success);
2978           }, function (error) {
2979             deferred.reject(error);
2980           });
2981         }, function (error) {
2982           deferred.reject(error);
2983         });
2984         return deferred.promise;
2985       };
2986
2987       service.getAggregatedData = function (functionId, docType, aggregation) {
2988         var deferred = $q.defer();
2989         service.getBase(functionId).then(function (success) {
2990           var databaseRequest = {
2991             method: 'POST',
2992             base: success.base,
2993             index: success.index,
2994             command: '_search',
2995             docType: docType,
2996             aggregation: aggregation
2997           };
2998           service.aggregationRequest(databaseRequest).then(function (success) {
2999             deferred.resolve(success);
3000           }, function (error) {
3001             deferred.reject(error);
3002           });
3003         }, function (error) {
3004           deferred.reject(error);
3005         });
3006         return deferred.promise;
3007       };
3008
3009       service.getAggregations = function (functionId, docType, aggregations) {
3010         var deferred = $q.defer();
3011         service.getBase(functionId).then(function (success) {
3012           var databaseRequest = {
3013             method: 'POST',
3014             base: success.base,
3015             index: success.index,
3016             command: '_search',
3017             docType: docType,
3018             aggregation: aggregations
3019           };
3020           service.aggregationRequest(databaseRequest).then(function (success) {
3021             deferred.resolve(success);
3022           }, function (error) {
3023             deferred.reject(error);
3024           });
3025         }, function (error) {
3026           deferred.reject(error);
3027         });
3028         return deferred.promise;
3029       };
3030
3031       service.getData = function (docType, from, size, sort, filter, query) {
3032         var deferred = $q.defer();
3033         service.getBase('mwtn').then(function (success) {
3034           var databaseRequest = {
3035             method: 'POST',
3036             base: success.base,
3037             index: success.index,
3038             docType: docType,
3039             from: from,
3040             size: size,
3041             sort: sort,
3042             filter: filter,
3043             query: query
3044           };
3045           service.genericRequest(databaseRequest).then(function (success) {
3046             deferred.resolve(success);
3047           }, function (error) {
3048             deferred.reject(error);
3049           });
3050         }, function (error) {
3051           deferred.reject(error);
3052         });
3053         return deferred.promise;
3054       };
3055
3056       service.deleteDocType = function (spec) {
3057         var deferred = $q.defer();
3058         service.getBase(spec.functionId).then(function (success) {
3059           var databaseRequest = {
3060             method: 'DELETE',
3061             base: success.base,
3062             index: success.index,
3063             docType: spec.docType,
3064             command: '_query',
3065             query: spec.query
3066           };
3067
3068           service.genericRequest(databaseRequest).then(function (success) {
3069             deferred.resolve(status);
3070           }, function (error) {
3071             deferred.reject(error);
3072           });
3073         }, function (error) {
3074           deferred.reject(error);
3075         });
3076         return deferred.promise;
3077       };
3078
3079       /**
3080        * A promise, which inquires a single document from database.
3081        * @param {string} functionId - An identifier of an SDN function (e.g 'mwtn').
3082        * @param {string} docType - The document type of the document to be deleted.
3083        * @param {string} id - An identifier of the document to be deleted.
3084        */
3085       service.getSingleDocument = function (functionId, docType, id) {
3086         var deferred = $q.defer();
3087         service.getBase(functionId).then(function (success) {
3088           var databaseRequest = {
3089             method: 'GET',
3090             base: success.base,
3091             index: success.index,
3092             docType: docType,
3093             command: id
3094           };
3095
3096           service.genericRequest(databaseRequest).then(function (success) {
3097             deferred.resolve(success.data._source);
3098           }, function (error) {
3099             deferred.reject(error);
3100           });
3101         }, function (error) {
3102           deferred.reject(error);
3103         });
3104         return deferred.promise;
3105       };
3106
3107       /**
3108        * A promise, creating or updateing a single document of the database.
3109        * @param {string} functionId - An identifier of an SDN function (e.g 'mwtn').
3110        * @param {string} docType - The document type of the document to be deleted.
3111        * @param {string} id - An identifier of the document to be deleted.
3112        * @param {Object} data - A json object to be stored in the database
3113        */
3114       service.createSingleDocument = function (functionId, docType, id, data) {
3115         var deferred = $q.defer();
3116         service.getBase(functionId).then(function (success) {
3117           var databaseRequest = {
3118             method: 'PUT',
3119             base: success.base,
3120             index: success.index,
3121             docType: docType,
3122             command: id,
3123             data: data
3124           };
3125
3126           service.genericRequest(databaseRequest).then(function (success) {
3127             deferred.resolve(success);
3128           }, function (error) {
3129             deferred.reject(error);
3130           });
3131         }, function (error) {
3132           deferred.reject(error);
3133         });
3134         return deferred.promise;
3135       };
3136
3137       /**
3138        * A promise, which deletes one document within the database.
3139        * @param {string} functionId - An identifier of an SDN function (e.g 'mwtn').
3140        * @param {string} docType - The document type of the document to be deleted.
3141        * @param {string} id - An identifier of the document to be deleted.
3142        */
3143       service.deleteSingleDocument = function (functionId, docType, id) {
3144         var deferred = $q.defer();
3145         service.getBase(functionId).then(function (success) {
3146           var databaseRequest = {
3147             method: 'DELETE',
3148             base: success.base,
3149             index: success.index,
3150             docType: docType,
3151             command: id,
3152           };
3153
3154           service.genericRequest(databaseRequest).then(function (success) {
3155             deferred.resolve({ status: success.status, logId: success.data._id });
3156           }, function (error) {
3157             deferred.reject(error);
3158           });
3159         }, function (error) {
3160           deferred.reject(error);
3161         });
3162         return deferred.promise;
3163       };
3164
3165       var moduleInformation;
3166       var inquireModuleInformation = function () {
3167         var deferred = $q.defer();
3168         service.getBase('mwtn').then(function (success) {
3169           var databaseRequest = {
3170             method: 'GET',
3171             base: success.base,
3172             index: success.index,
3173             from: 0,
3174             size: 999
3175           };
3176           service.getAllData('mwtn', 'module', 0, 999, undefined).then(function (success) {
3177             // console.log(JSON.stringify(data.data.hits.hits));
3178             moduleInformation = {};
3179             success.data.hits.hits.map(function (hit) {
3180               moduleInformation[hit._id] = hit._source;
3181             });
3182             // console.log('got moduleInformation', Object.keys(moduleInformation).length);
3183             return deferred.resolve(moduleInformation);
3184           }, function (error) {
3185             deferred.reject(error);
3186           });
3187         }, function (error) {
3188           deferred.reject(error);
3189         });
3190         return deferred.promise;
3191       };
3192
3193       /**
3194        * A promise which returns object yang class and attribute descriptions
3195        */
3196       service.getModules = function () {
3197         var deferred = $q.defer();
3198         if (moduleInformation) {
3199           deferred.resolve(moduleInformation);
3200         } else {
3201           inquireModuleInformation().then(function (success) {
3202             deferred.resolve(success);
3203           }, function (error) {
3204             deferred.reject(error);
3205           });
3206         }
3207         return deferred.promise;
3208       };
3209
3210       var schemaInformation;
3211       var inquireSchemaInformation = function () {
3212         var deferred = $q.defer();
3213         service.getBase('mwtn').then(function (success) {
3214           var databaseRequest = {
3215             method: 'GET',
3216             base: success.base,
3217             index: success.index,
3218             from: 0,
3219             size: 999
3220           };
3221           service.getAllData('mwtn', 'schema-information', 0, 9999, undefined).then(function (success) {
3222             // console.log(JSON.stringify(data.data.hits.hits));
3223             schemaInformation = {};
3224             success.data.hits.hits.map(function (hit) {
3225               schemaInformation[hit._id] = hit._source;
3226             });
3227             // console.log('got schemaInformation', Object.keys(schemaInformation).length);
3228             return deferred.resolve(schemaInformation);
3229           }, function (error) {
3230             deferred.reject(error);
3231           });
3232         }, function (error) {
3233           deferred.reject(error);
3234         });
3235         return deferred.promise;
3236       };
3237
3238       /**
3239        * A promise which returns object yang class and attribute descriptions
3240        */
3241       service.getSchema = function () {
3242         var deferred = $q.defer();
3243         if (schemaInformation) {
3244           deferred.resolve(schemaInformation);
3245         } else {
3246           inquireSchemaInformation().then(function (success) {
3247             deferred.resolve(success);
3248           }, function (error) {
3249             deferred.reject(error);
3250           });
3251         }
3252         return deferred.promise;
3253       };
3254
3255       return service;
3256     });
3257     // Class NetConfServer
3258     mwtnCommonsApp.register.factory('NetConfServer', function () {
3259       // Classes
3260       // Class NetConfServer
3261       var NetConfServer = function (data) {
3262         this.data = data;
3263         this.getData = function () {
3264           return this.data;
3265         };
3266         this.getRadioSignalId = function () {
3267           return this.data.airInterfaceConfiguration.radioSignalId;
3268         };
3269         this.isLinkUp = function () {
3270           return this.data.airInterfaceStatus.linkIsUp;
3271         };
3272         this.isPowerOn = function () {
3273           return this.data.airInterfaceConfiguration.powerIsOn;
3274         };
3275         this.isActive = function () {
3276           return this.isPowerOn() && this.isLinkUp();
3277         };
3278       };
3279       return NetConfServer;
3280     });
3281
3282     mwtnCommonsApp.register.factory('ActualNetworkElement', function () {
3283       // Classes
3284       // Class ActualNetworkElement
3285       var ActualNetworkElement = function (data) {
3286         this.data = data;
3287         this.data.layerProtocols = {};
3288         this.setOnfNetworkElement = function (onfNe) {
3289           this.data.onfNetworkElement = onfNe;
3290         };
3291         this.getLpByRadioSignalId = function (radioSignalId) {
3292           //console.log('getLP', JSON.stringify(this.data.ltp));
3293           var layerProtocol;
3294           for (var layerProtocolKey in this.data.layerProtocols) {
3295             if (this.data.layerProtocols[layerProtocolKey].getRadioSignalId &&
3296               radioSignalId === parseInt(this.data.layerProtocols[layerProtocolKey].getRadioSignalId())) {
3297               layerProtocol = this.data.layerProtocols[layerProtocolKey];
3298             }
3299           }
3300           return layerProtocol;
3301         };
3302         this.getLpByRadioSignalIds = function (radioSignalIds) {
3303           //console.log('getLP', JSON.stringify(this.data.ltp));
3304           var layerProtocol;
3305           if (radioSignalIds !== undefined) {
3306             for (var layerProtocolKey in this.data.layerProtocols) {
3307               if (this.data.layerProtocols[layerProtocolKey].getRadioSignalIds &&
3308                 radioSignalIds.toString() === this.data.layerProtocols[layerProtocolKey].getRadioSignalIds(this).toString()) {
3309                 layerProtocol = this.data.layerProtocols[layerProtocolKey];
3310               }
3311             }
3312           }
3313           return layerProtocol;
3314         };
3315         this.setLP = function (onfPac) {
3316           this.data.layerProtocols[onfPac.data.layerProtocol] = onfPac;
3317         };
3318         this.getLpPac = function (lpRef) {
3319           return this.data.layerProtocols[lpRef];
3320         };
3321         this.getName = function () {
3322           return this.data.name;
3323         };
3324         this.getConnectionStatus = function () {
3325           return this.data.connectionStatus;
3326         };
3327         this.isConnected = function () {
3328           return this.data.name !== 'controller-config' && this.data.connectionStatus == 'connected';
3329         };
3330         this.setConnectionStatus = function (status) {
3331           this.data.connectionStatus = status;
3332         };
3333       };
3334       return ActualNetworkElement;
3335     });
3336
3337     mwtnCommonsApp.register.factory('LogicalTerminationPoint', function ($mwtnGlobal, $mwtnLog) {
3338       // Sub-Class LayerProtocol
3339       /**
3340         * An object representing an LP.
3341         * @typedef {Object} LayerProtocol
3342         */
3343       var LayerProtocol = function (data) {
3344         // take a guess, if termiation-state is not exposed
3345         if (data['termination-state'] === undefined) {
3346
3347           data['termination-state'] = 'terminated-bidirectional';
3348           if (data['layer-protocol-name'] === "ETH") {
3349             data['termination-state'] = 'lp-can-never-terminate';
3350           }
3351           $mwtnLog.warning({ component: 'LTP.getTerminationState', message: 'Check whether NE provided mandatory termination state. ' + data.uuid });
3352         } else if (data['termination-state'] === false || data['termination-state'] === 'false' ) {
3353           data['termination-state'] = 'terminated-bidirectional';
3354         }
3355
3356         // console.log('in', JSON.stringify(data));
3357         var defaultMapping = {
3358           'ety-ttp': {},
3359           // [FFA 1709] 'etc-ttp': { 'capability': 'urn:onf:params:xml:ns:yang:microwave-model?module=microwave-model', 'revision': '2017-03-24', 'conditional-package': 'mw-ethernet-container-pac' },
3360           'etc-ttp': { 'capability': 'uri:onf:MicrowaveModel-ObjectClasses-EthernetContainerl?module=MicrowaveModel-ObjectClasses-EthernetContainer', 'revision': '2016-09-02', 'conditional-package': 'MW_EthernetContainer_Pac' },
3361           'tdm-ctp': { 'capability': 'urn:onf:params:xml:ns:yang:microwave-model?module=microwave-model', 'revision': '2017-03-24', 'conditional-package': 'mw-tdm-container-pac' },
3362
3363           // due to E/// 
3364           'mws-ctp': { 'capability': 'uri:onf:MicrowaveModel-ObjectClasses-PureEthernetStructure?module=MicrowaveModel-ObjectClasses-PureEthernetStructure', 'revision': '2016-09-02', 'conditional-package': 'MW_PureEthernetStructure_Pac' },
3365           'mwps-ctp': { 'capability': 'uri:onf:MicrowaveModel-ObjectClasses-AirInterface?module=MicrowaveModel-ObjectClasses-AirInterface', 'revision': '2016-09-01', 'conditional-package': 'MW_AirInterface_Pac' },
3366           // 'mws-ctp': { 'capability': 'urn:onf:params:xml:ns:yang:microwave-model?module=microwave-model', 'revision': '2017-03-24', 'conditional-package': 'mw-air-interface-diversity-pac' },
3367
3368           'eth-ctp': { 'capability': 'urn:onf:params:xml:ns:yang:onf-ethernet-conditional-packages?module=onf-ethernet-conditional-packages', 'revision': '2017-04-02', 'conditional-package': 'ethernet-pac' },
3369           // 3rd PoC
3370           'mwps-ttp': { 'capability': 'uri:onf:MicrowaveModel-ObjectClasses-AirInterface?module=MicrowaveModel-ObjectClasses-AirInterface', 'revision': '2016-09-01', 'conditional-package': 'MW_AirInterface_Pac' },
3371           'mws-ttp': { 'capability': 'uri:onf:MicrowaveModel-ObjectClasses-PureEthernetStructure?module=MicrowaveModel-ObjectClasses-PureEthernetStructure', 'revision': '2016-09-02', 'conditional-package': 'MW_PureEthernetStructure_Pac' },
3372           'eth-ctp-ctp': { 'capability': 'uri:onf:MicrowaveModel-ObjectClasses-EthernetContainerl?module=MicrowaveModel-ObjectClasses-EthernetContainer', 'revision': '2016-09-02', 'conditional-package': 'MW_EthernetContainer_Pac' },
3373           'etc-ctp': { 'capability': 'uri:onf:MicrowaveModel-ObjectClasses-EthernetContainerl?module=MicrowaveModel-ObjectClasses-EthernetContainer', 'revision': '2016-09-02', 'conditional-package': 'MW_EthernetContainer_Pac' },
3374
3375         };
3376         // create empty extension, if not exists
3377         if (!data.extension) {
3378           data.extension = [];
3379         }
3380         this.data = data;
3381
3382         // methods
3383         this.getData = function () {
3384           return this.data;
3385         };
3386         this.getId = function () {
3387           return this.getData().uuid;
3388         };
3389         this.getLabel = function () {
3390           return ['LP(', this.getItuLabel(true).toUpperCase(), '): ', this.getId()].join('');
3391         };
3392         this.getLayer = function () {
3393           var layer = this.getData()['layer-protocol-name'];
3394           if (layer === 'ETH-CTP') {
3395             layer = 'ETC';
3396           }
3397           return layer;
3398         };
3399         this.getTerminationState = function (abstract) {
3400           // 3th PoC termination state is of type boolean 
3401           if (this.getData()['termination-state'] === true) {
3402             return 'ttp';
3403           } else if (this.getData()['termination-state'] === false) {
3404             return 'ctp';
3405           }
3406           // 4th PoC termination state is of type enum
3407           if (abstract !== true) {
3408             return this.getData()['termination-state'];
3409           }
3410           var mapping = {
3411             'lp-can-never-terminate': 'ctp',
3412             'lt-not-terminated': 'ctp',
3413             'terminated-server-to-client-flow': 'ttp',
3414             'terminated-client-to-server-flow': 'ttp',
3415             'terminated-bidirectional': 'ttp',
3416             'lt-permenantly-terminated': 'ttp',
3417             'termination-state-unknown': 'ttp'
3418           };
3419           return mapping[this.getData()['termination-state']];
3420         };
3421         this.getItuLabel = function () {
3422           return [this.getLayer(), this.getTerminationState(true)].join('-').toLowerCase();
3423         };
3424         this.getExtension = function (key) {
3425           var result = this.getData().extension.filter(function (ex) {
3426             return ex['value-name'] === key;
3427           }).map(function (ex) {
3428             return ex.value;
3429           });
3430           if (result && result.length > 0) {
3431             return result[0];
3432           }
3433           // check hardcoded alternatives
3434           var ituLabel = this.getItuLabel();
3435           if (!defaultMapping[ituLabel]) {
3436             return '';
3437           }
3438           return defaultMapping[ituLabel][key];
3439         };
3440         /**
3441          * A getter for the yang-capability of the LayerProtocol
3442          * @param {boolean|undefined} moduleOnly - Defines, whether the result should contain only yang-module-name.
3443          * @return {string} The conditional package name of the LayerProtocol
3444          */
3445         this.getCapability = function (moduleOnly) {
3446           var cap = this.getExtension('capability');
3447           if (!cap) return '';
3448
3449           // workaround bug in spec
3450           if (cap.contains('onf-ethernet-conditional-package') && !cap.contains('onf-ethernet-conditional-packages')) {
3451             console.warn('Inform vendor about spec error and updates');
3452             cap = cap.replaceAll('onf-ethernet-conditional-package', 'onf-ethernet-conditional-packages');
3453           }
3454           if (cap !== '' && moduleOnly === true) {
3455             return cap.split('?module=')[1];
3456           }
3457           return cap;
3458         };
3459         this.getRevision = function () {
3460           return this.getExtension('revision');
3461         };
3462         /**
3463          * A getter for the conditional package name of the LayerProtocol
3464          * @param {boolean|undefined} addModule - Defines, whether the result should include the yang-module-name.
3465          * @return {string} The conditional package name of the LayerProtocol
3466          */
3467         this.getConditionalPackage = function (addModule) {
3468           var cp = this.getExtension('conditional-package');
3469           if (!cp) {
3470             return '';
3471           }
3472           if (addModule === true) {
3473             return [this.getCapability(true), cp].join(':');
3474           }
3475           return cp;
3476         };
3477       };
3478
3479       // Sub-Class LogicalTerminationPoint
3480       /**
3481         * An object representing an LTP.
3482         * @typedef {Object} LogicalTerminationPoint
3483         */
3484       var LogicalTerminationPoint = function (data) {
3485         this.data = data;
3486         this.layerProtocols = this.data.lp.map(function (layerProtocol) {
3487           return new LayerProtocol(layerProtocol);
3488         });
3489         // console.log(this.layerProtocols);
3490         this.getData = function () {
3491           return this.data;
3492         };
3493         this.getId = function () {
3494           return this.getData().uuid;
3495         };
3496         this.getName = function () {
3497           return this.getData().name[0].value;
3498         };
3499         this.getDirectionality = function () {
3500           return this.getData()['ltp-direction'];
3501         };
3502         this.getServerLtps = function () {
3503           return this.getData()['server-ltp'];
3504         };
3505         this.getClientLtps = function () {
3506           return this.getData()['client-ltp'];
3507         };
3508         this.getLayer = function () {
3509           return this.getLayerProtocols()[0].getLayer();
3510         };
3511         this.getLabel = function () {
3512           return ['LTP(', this.getLayerProtocols()[0].getItuLabel(true).toUpperCase(), '): ', this.getId()].join('');
3513         };
3514         this.getLayerProtocols = function () {
3515           return this.layerProtocols;
3516         };
3517         this.getConditionalPackages = function () {
3518           console.error(JSON.stringifythis.getLayerProtocols()());
3519           return this.getLayerProtocols().map(function (lp) {
3520             return lp.getConditionalPackage();
3521           });
3522         };
3523       }
3524       return LogicalTerminationPoint;
3525     });
3526
3527     mwtnCommonsApp.register.factory('PtpClock', function (PtpPort) {
3528       var PtpClock = function (data) {
3529         var COMPONENT = "PtpClock";
3530         this.data = {};
3531         if (data && data['instance-list'] && data['instance-list'][0]) {
3532           this.data = data['instance-list'][0];
3533         }
3534         if (!this.data['port-ds-list'] || this.data['port-ds-list'].length === 0) {
3535           this.ptpPorts = [];
3536           var message = ['The PTP clock', data['instance-number'], 'dose not support a single PTP port.'].join(' ');
3537           console.info({ component: COMPONENT, message: message });
3538         } else {
3539           this.ptpPorts = this.data['port-ds-list'].map(function(ptpPort) {
3540             return new PtpPort(ptpPort);
3541           });
3542         }
3543
3544         this.getData = function () {
3545           return this.data;
3546           // return JSON.parse(JSON.stringify(this.data).replaceAll('onf-ptp-dataset:', ''));
3547         };
3548         this.getIdentity = function(hex) {
3549           var defaultDs = this.getData()['default-ds'];
3550           var result = 'ERROR: no clock identity found';
3551           if (defaultDs && defaultDs['clock-identity']) {
3552             result = defaultDs['clock-identity'];
3553             if (hex) {
3554               result = result.base64ToHex();
3555             }
3556           }
3557           return result;
3558         };
3559         this.getParent = function(hex) {
3560           var parentDs = this.getData()['parent-ds'];
3561           var key = 'parent-port-identity';
3562           var result = '';
3563           if (parentDs && parentDs[key] && parentDs[key]['clock-identity'] && parentDs[key]['port-number']) {
3564             result = parentDs[key]['clock-identity'];
3565             if (hex) {
3566               result = result.base64ToHex();
3567             }
3568             result = [result, parentDs[key]['port-number']].join('#');
3569           }
3570           return result;
3571         };
3572         this.getGrandMaster = function(hex) {
3573           var parentDs = this.getData()['parent-ds'];
3574           // console.warn(JSON.stringify(parentDs));
3575           var key = 'parent-port-identity';
3576           var result = '';
3577           if (parentDs && parentDs['grandmaster-identity']) {
3578             result = parentDs['grandmaster-identity'];
3579             if (hex) {
3580               result = result.base64ToHex();
3581             }
3582           }
3583           return result;
3584         };
3585         this.getPtpPorts = function () {
3586           return this.ptpPorts;
3587         };
3588       };
3589       return PtpClock;
3590     });
3591
3592     mwtnCommonsApp.register.factory('PtpPort', function () {
3593       var PtpPort = function (data) {
3594         this.data = data;
3595         this.getData = function () {
3596           return this.data;
3597         };
3598         this.getId = function() {
3599           return this.getData()['port-number'];
3600         };
3601         this.getNumber = function() {
3602           return this.getData()['port-number'];
3603         };
3604         this.getState = function() {
3605           return this.getData()['port-state'];
3606         };
3607         this.isSlave = function() {
3608           return this.getData()['port-state'] === 'SLAVE';
3609         };
3610         this.isMaster = function() {
3611           return this.getData()['port-state'] === 'MASTER';
3612         };
3613         this.getLogicalTerminationPointReference = function() {
3614           return this.getData()['onf-ptp-dataset:logical-termination-point'];
3615         };
3616       };
3617       return PtpPort;
3618     });
3619
3620     mwtnCommonsApp.register.factory('OnfNetworkElement', function ($mwtnGlobal, LogicalTerminationPoint) {
3621       // Classes
3622       // Class OnfNetworkElement
3623       var OnfNetworkElement = function (data) {
3624         var COMPONENT = "OnfNetworkElement";
3625
3626         this.data = data;
3627         if (!this.data) {
3628           var message = ['No data received.'].join(' ');
3629           console.warn({ component: COMPONENT, message: message });
3630           return;
3631         }
3632
3633         // console.log(JSON.stringify(data));
3634         if (!this.data.ltp || this.data.ltp.length === 0) {
3635           this.logicalTerminationPoints = [];
3636           var message = ['The network-element', data.uuid, 'does not support a single LTP. No LTP -> no SDN integration via ONF core-model.'].join(' ');
3637           // TODO $mwtnLog -> Unknown provider: $mwtnlogProvider <- $mwtnlog <- OnfNetworkElement [sko] Dont get it ;(
3638           console.warn({ component: COMPONENT, message: message });
3639         } else {
3640           this.logicalTerminationPoints = this.data.ltp.map(function (logicalTerminationPoint) {
3641             return new LogicalTerminationPoint(logicalTerminationPoint);
3642           });
3643         }
3644
3645         this.getData = function () {
3646           return this.data;
3647         };
3648         this.getId = function () {
3649           return this.getData().uuid;
3650         };
3651         this.getForwardingDomain = function () {
3652           return this.getData().fd;
3653         };
3654         this.getName = function () {
3655           return this.getData().name[0].value || this.getData().uuid;
3656         };
3657         this.getLogicalTerminationPoints = function () {
3658           return this.logicalTerminationPoints;
3659         };
3660         this.getLtp = function (id) {
3661           var result = this.getLogicalTerminationPoints().filter(function (ltp) {
3662             return ltp.getId() === id;
3663           });
3664           if (result.length === 1) {
3665             return result[0];
3666           }
3667           return undefined;
3668         };
3669         this.getLpById = function (id) {
3670           var result = {};
3671           this.getLogicalTerminationPoints().map(function (ltp) {
3672             ltp.getLayerProtocols().map(function (lp) {
3673               if (lp.getData().uuid === id) {
3674                 result = lp;
3675               }
3676             });
3677           });
3678           return result;
3679         };
3680         this.getNumberOfLtps = function () {
3681           return this.logicalTerminationPoints.length;
3682         };
3683         this.getServerLtps = function (layerProtocolRef) {
3684           var result = [];
3685           if (this.data._ltpRefList) {
3686             var ltpList = this.data._ltpRefList.map(function (ltp) {
3687               if (ltp.lp[0].uuid === layerProtocolRef) {
3688                 result = ltp._serverLtpRefList;
3689               }
3690             });
3691           }
3692           return result;
3693         };
3694         this.getClientLtpIds = function (layerProtocolRef) {
3695           var result = [];
3696           if (this.data._ltpRefList) {
3697             var ltpList = this.data._ltpRefList.map(function (ltp) {
3698               if (ltp.lp[0].uuid === layerProtocolRef) {
3699                 result = ltp._clientLtpRefList;
3700               }
3701             });
3702           }
3703           return result;
3704         };
3705         this.getLpByLtpRef = function (ltpRef) {
3706           var result;
3707           if (this.data._ltpRefList) {
3708             var ltpList = this.data._ltpRefList.map(function (ltp) {
3709               if (ltp.uuid === ltpRef) {
3710                 result = ltp.lp[0];
3711               }
3712             });
3713           }
3714           return result;
3715         };
3716         this.getLtpsByLayer = function (layerProtocolName) {
3717           return this.getLogicalTerminationPoints().filter(function (ltp) {
3718             return ltp.getLayer() === layerProtocolName;
3719           });
3720         };
3721         this.getLTPMwpsList = function () {
3722           return this.getLtpsByLayer('MWPS');
3723         };
3724         this.getLTPMwsList = function () {
3725           return this.getLtpsByLayer('MWS');
3726         };
3727         this.getLTPEthCtpList = function () {
3728           return this.getLtpsByLayer('ETH');
3729         };
3730         this.getLTPTdmCtpList = function () {
3731           return this.getLtpsByLayer('TDM');
3732         };
3733       };
3734       return OnfNetworkElement;
3735     });
3736
3737     mwtnCommonsApp.register.factory('MicrowavePhysicalSection', function () {
3738       // Classes
3739       // Class MicrowavePhysicalSection
3740       var MicrowavePhysicalSection = function (data) {
3741         this.data = data;
3742         this.getData = function () {
3743           return this.data;
3744         };
3745         this.getLayerProtocolId = function () {
3746           return this.getData().layerProtocol;
3747         };
3748         this.getRadioSignalId = function () {
3749           return this.getData()['air-interface-configuration'] ? this.data['air-interface-configuration']['radio-signal-id'] : -1;
3750         };
3751         this.isLinkUp = function () {
3752           return this.getData()['air-interface-status']['link-is-up'];
3753         };
3754         this.isPowerOn = function () {
3755           return this.getData()['air-interface-configuration']['power-is-on'];
3756         };
3757         this.isActive = function () {
3758           return this.isPowerOn() && this.isLinkUp();
3759         };
3760       };
3761       return MicrowavePhysicalSection;
3762     });
3763
3764     mwtnCommonsApp.register.factory('MicrowaveSection', function () {
3765       // Classes
3766       // Class MicrowaveSection
3767       var MicrowaveSection = function (data) {
3768         this.data = data;
3769         this.getData = function () {
3770           return this.data;
3771         };
3772         this.getId = function () {
3773           return this.data.layerProtocol;
3774         };
3775         this.getRadioSignalIds = function (actualNe) {
3776           this.data.parent = actualNe;
3777           var result = [];
3778           var onfNe = actualNe.data.onfNetworkElement;
3779           var lpId = this.getId();
3780           onfNe.getServerLtps(lpId).map(function (mwpsLtpRef) {
3781             var lpRef = onfNe.getLpByLtpRef(mwpsLtpRef).uuid;
3782             var mwps = actualNe.getLpPac(lpRef);
3783             result.push(mwps.getRadioSignalId());
3784           });
3785           return result;
3786         };
3787         this.getTimeSlotCapacity = function () {
3788           return this.data.structureCapability.timeSlotCapacity;
3789         };
3790         this.getTotalNumberOfTimeSlots = function () {
3791           return this.data.structureCapability.totalNumberOfTimeSlots;
3792         };
3793         this.getNumberOfEffectiveTimeSlots = function () {
3794           var count = 0;
3795           this.data.structureStatus.timeSlotStatusList.map(function (ts) {
3796             if (ts.operationalStatus === 'ENABLED') {
3797               count = count + 1;
3798             }
3799           });
3800           return count;
3801         };
3802         this.getConfiguredCapacity = function () {
3803           return this.getTotalNumberOfTimeSlots() * this.getTimeSlotCapacity();
3804         };
3805         this.getEffectiveCapacity = function () {
3806           return this.getNumberOfEffectiveTimeSlots() * this.getTimeSlotCapacity();
3807         };
3808         this.isActive = function () {
3809           if (this.data.parent === undefined) {
3810             return false;
3811           }
3812           var actualNe = this.data.parent;
3813           var result = true;
3814           var onfNe = actualNe.data.onfNetworkElement;
3815           var lpId = this.getId();
3816           onfNe.getServerLtps(lpId).map(function (mwpsLtpRef) {
3817             var lpRef = onfNe.getLpByLtpRef(mwpsLtpRef).uuid;
3818             var mwps = actualNe.getLpPac(lpRef);
3819             result = result && mwps.isActive();
3820           });
3821           return result;
3822         };
3823       };
3824       return MicrowaveSection;
3825     });
3826   });