Commit 68e9844f535c7ec6e5527143d696157319b11389

Authored by Vladyslav_Prykhodko
1 parent 6148dafa

Add device

... ... @@ -30,7 +30,9 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
30 30 subscribeForEntityAttributes: subscribeForEntityAttributes,
31 31 unsubscribeForEntityAttributes: unsubscribeForEntityAttributes,
32 32 saveEntityAttributes: saveEntityAttributes,
33   - deleteEntityAttributes: deleteEntityAttributes
  33 + deleteEntityAttributes: deleteEntityAttributes,
  34 + saveEntityTimeseries: saveEntityTimeseries,
  35 + deleteEntityTimeseries: deleteEntityTimeseries
34 36 }
35 37
36 38 return service;
... ... @@ -212,7 +214,8 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
212 214 }
213 215 }
214 216
215   - function saveEntityAttributes(entityType, entityId, attributeScope, attributes) {
  217 + function saveEntityAttributes(entityType, entityId, attributeScope, attributes, config) {
  218 + config = config || {};
216 219 var deferred = $q.defer();
217 220 var attributesData = {};
218 221 var deleteAttributes = [];
... ... @@ -229,7 +232,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
229 232 }
230 233 if (Object.keys(attributesData).length) {
231 234 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope;
232   - $http.post(url, attributesData).then(function success(response) {
  235 + $http.post(url, attributesData, config).then(function success(response) {
233 236 if (deleteEntityAttributesPromise) {
234 237 deleteEntityAttributesPromise.then(
235 238 function success() {
... ... @@ -260,6 +263,55 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
260 263 return deferred.promise;
261 264 }
262 265
  266 + function saveEntityTimeseries(entityType, entityId, timeseriesScope, timeseries, config) {
  267 + config = config || {};
  268 + var deferred = $q.defer();
  269 + var timeseriesData = {};
  270 + var deleteTimeseries = [];
  271 + for (var a=0; a<timeseries.length;a++) {
  272 + if (angular.isDefined(timeseries[a].value) && timeseries[a].value !== null) {
  273 + timeseriesData[timeseries[a].key] = timeseries[a].value;
  274 + } else {
  275 + deleteTimeseries.push(timeseries[a]);
  276 + }
  277 + }
  278 + var deleteEntityTimeseriesPromise;
  279 + if (deleteTimeseries.length) {
  280 + deleteEntityTimeseriesPromise = deleteEntityTimeseries(entityType, entityId, deleteTimeseries);
  281 + }
  282 + if (Object.keys(timeseriesData).length) {
  283 + var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/' + timeseriesScope;
  284 + $http.post(url, timeseriesData, config).then(function success(response) {
  285 + if (deleteEntityTimeseriesPromise) {
  286 + deleteEntityTimeseriesPromise.then(
  287 + function success() {
  288 + deferred.resolve(response.data);
  289 + },
  290 + function fail() {
  291 + deferred.reject();
  292 + }
  293 + )
  294 + } else {
  295 + deferred.resolve(response.data);
  296 + }
  297 + }, function fail() {
  298 + deferred.reject();
  299 + });
  300 + } else if (deleteEntityTimeseriesPromise) {
  301 + deleteEntityTimeseriesPromise.then(
  302 + function success() {
  303 + deferred.resolve();
  304 + },
  305 + function fail() {
  306 + deferred.reject();
  307 + }
  308 + )
  309 + } else {
  310 + deferred.resolve();
  311 + }
  312 + return deferred.promise;
  313 + }
  314 +
263 315 function deleteEntityAttributes(entityType, entityId, attributeScope, attributes) {
264 316 var deferred = $q.defer();
265 317 var keys = '';
... ... @@ -278,5 +330,22 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
278 330 return deferred.promise;
279 331 }
280 332
  333 + function deleteEntityTimeseries(entityType, entityId, timeseries) {
  334 + var deferred = $q.defer();
  335 + var keys = '';
  336 + for (var i = 0; i < timeseries.length; i++) {
  337 + if (i > 0) {
  338 + keys += ',';
  339 + }
  340 + keys += timeseries[i].key;
  341 + }
  342 + var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/delete' + '?keys=' + keys;
  343 + $http.delete(url).then(function success() {
  344 + deferred.resolve();
  345 + }, function fail() {
  346 + deferred.reject();
  347 + });
  348 + return deferred.promise;
  349 + }
281 350
282 351 }
\ No newline at end of file
... ...
... ... @@ -31,6 +31,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
31 31 getDeviceCredentials: getDeviceCredentials,
32 32 getTenantDevices: getTenantDevices,
33 33 saveDevice: saveDevice,
  34 + saveDeviceParameters: saveDeviceParameters,
34 35 saveDeviceCredentials: saveDeviceCredentials,
35 36 unassignDeviceFromCustomer: unassignDeviceFromCustomer,
36 37 makeDevicePublic: makeDevicePublic,
... ... @@ -42,7 +43,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
42 43 sendOneWayRpcCommand: sendOneWayRpcCommand,
43 44 sendTwoWayRpcCommand: sendTwoWayRpcCommand,
44 45 findByQuery: findByQuery,
45   - getDeviceTypes: getDeviceTypes
  46 + getDeviceTypes: getDeviceTypes,
  47 + findByName: findByName
46 48 }
47 49
48 50 return service;
... ... @@ -124,7 +126,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
124 126 if (!config) {
125 127 config = {};
126 128 }
127   - config = Object.assign(config, { ignoreErrors: ignoreErrors });
  129 + config = Object.assign(config, {ignoreErrors: ignoreErrors});
128 130 $http.get(url, config).then(function success(response) {
129 131 deferred.resolve(response.data);
130 132 }, function fail(response) {
... ... @@ -136,8 +138,8 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
136 138 function getDevices(deviceIds, config) {
137 139 var deferred = $q.defer();
138 140 var ids = '';
139   - for (var i=0;i<deviceIds.length;i++) {
140   - if (i>0) {
  141 + for (var i = 0; i < deviceIds.length; i++) {
  142 + if (i > 0) {
141 143 ids += ',';
142 144 }
143 145 ids += deviceIds[i];
... ... @@ -146,11 +148,11 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
146 148 $http.get(url, config).then(function success(response) {
147 149 var devices = response.data;
148 150 devices.sort(function (device1, device2) {
149   - var id1 = device1.id.id;
150   - var id2 = device2.id.id;
151   - var index1 = deviceIds.indexOf(id1);
152   - var index2 = deviceIds.indexOf(id2);
153   - return index1 - index2;
  151 + var id1 = device1.id.id;
  152 + var id2 = device2.id.id;
  153 + var index1 = deviceIds.indexOf(id1);
  154 + var index2 = deviceIds.indexOf(id2);
  155 + return index1 - index2;
154 156 });
155 157 deferred.resolve(devices);
156 158 }, function fail(response) {
... ... @@ -159,10 +161,11 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
159 161 return deferred.promise;
160 162 }
161 163
162   - function saveDevice(device) {
  164 + function saveDevice(device, config) {
  165 + config = config || {};
163 166 var deferred = $q.defer();
164 167 var url = '/api/device';
165   - $http.post(url, device).then(function success(response) {
  168 + $http.post(url, device, config).then(function success(response) {
166 169 deferred.resolve(response.data);
167 170 }, function fail() {
168 171 deferred.reject();
... ... @@ -170,6 +173,54 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
170 173 return deferred.promise;
171 174 }
172 175
  176 + function saveDeviceRelarion(deviceId, deviceRelation, config) {
  177 + var deferred = $q.defer();
  178 + var attributesType = Object.keys(types.attributesScope);
  179 + var allPromise = [];
  180 + var promise = "";
  181 + for (var i = 0; i < attributesType.length; i++) {
  182 + if (deviceRelation.attributes[attributesType[i]] && deviceRelation.attributes[attributesType[i]].length !== 0) {
  183 + promise = attributeService.saveEntityAttributes(types.entityType.device, deviceId, types.attributesScope[attributesType[i]].value, deviceRelation.attributes[attributesType[i]], config).then(function success() {});
  184 + allPromise.push(promise);
  185 + }
  186 + }
  187 + if (deviceRelation.timeseries.length !== 0) {
  188 + promise = attributeService.saveEntityTimeseries(types.entityType.device, deviceId, "time", deviceRelation.timeseries, config).then(function success() {
  189 + });
  190 + allPromise.push(promise);
  191 + }
  192 + $q.all(allPromise).then(function success() {
  193 + deferred.resolve();
  194 + });
  195 + return deferred.promise;
  196 + }
  197 +
  198 + function saveDeviceParameters(deviceParameters, update, config) {
  199 + config = config || {};
  200 + var deferred = $q.defer();
  201 + var newDevice = {
  202 + name: deviceParameters.name,
  203 + type: deviceParameters.type
  204 + };
  205 + saveDevice(newDevice, config).then(function success(response) {
  206 + saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success() {
  207 + deferred.resolve();
  208 + });
  209 + }, function fail() {
  210 + if (update) {
  211 + findByName(deviceParameters.name, config).then(function success(response) {
  212 + saveDeviceRelarion(response.id.id, deviceParameters, config).then(function success() {
  213 + deferred.resolve();
  214 + });
  215 + });
  216 + } else {
  217 + deferred.resolve();
  218 + }
  219 + console.log("error"); // eslint-disable-line
  220 + });
  221 + return deferred.promise;
  222 + }
  223 +
173 224 function deleteDevice(deviceId) {
174 225 var deferred = $q.defer();
175 226 var url = '/api/device/' + deviceId;
... ... @@ -297,7 +348,7 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
297 348 if (!config) {
298 349 config = {};
299 350 }
300   - config = Object.assign(config, { ignoreErrors: ignoreErrors });
  351 + config = Object.assign(config, {ignoreErrors: ignoreErrors});
301 352 $http.post(url, query, config).then(function success(response) {
302 353 deferred.resolve(response.data);
303 354 }, function fail() {
... ... @@ -317,4 +368,15 @@ function DeviceService($http, $q, $window, userService, attributeService, custom
317 368 return deferred.promise;
318 369 }
319 370
  371 + function findByName(deviceName, config) {
  372 + config = config || {};
  373 + var deferred = $q.defer();
  374 + var url = '/api/tenant/devices?deviceName=' + deviceName;
  375 + $http.get(url, config).then(function success(response) {
  376 + deferred.resolve(response.data);
  377 + }, function fail() {
  378 + deferred.reject();
  379 + });
  380 + return deferred.promise;
  381 + }
320 382 }
... ...
... ... @@ -350,6 +350,40 @@ export default angular.module('thingsboard.types', [])
350 350 rulenode: "RULE_NODE",
351 351 entityView: "ENTITY_VIEW"
352 352 },
  353 + entityGroup: {
  354 + columnType: {
  355 + sharedAttribute: {
  356 + name: 'entity-group.column-type.shared-attribute',
  357 + value: 'SHARED_ATTRIBUTE'
  358 + },
  359 + serverAttribute: {
  360 + name: 'entity-group.column-type.server-attribute',
  361 + value: 'SERVER_ATTRIBUTE'
  362 + },
  363 + timeseries: {
  364 + name: 'entity-group.column-type.timeseries',
  365 + value: 'TIMESERIES'
  366 + },
  367 + entityField: {
  368 + name: 'entity-group.column-type.entity-field',
  369 + value: 'ENTITY_FIELD'
  370 + }
  371 + },
  372 + entityField: {
  373 + name: {
  374 + name: 'entity-group.entity-field.name',
  375 + value: 'name'
  376 + },
  377 + type: {
  378 + name: 'entity-group.entity-field.type',
  379 + value: 'type'
  380 + },
  381 + assigned_customer: {
  382 + name: 'entity-group.entity-field.assigned_customer',
  383 + value: 'assigned_customer'
  384 + }
  385 + }
  386 + },
353 387 aliasEntityType: {
354 388 current_customer: "CURRENT_CUSTOMER"
355 389 },
... ...
... ... @@ -16,7 +16,7 @@
16 16 import './import-dialog.scss';
17 17
18 18 /*@ngInject*/
19   -export default function ImportDialogCsvController($scope, $mdDialog, toast, importTitle, importFileLabel) {
  19 +export default function ImportDialogCsvController($scope, $mdDialog, toast, importTitle, importFileLabel, importExport, types, $timeout) {
20 20
21 21 var vm = this;
22 22
... ... @@ -25,9 +25,58 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
25 25 vm.fileAdded = fileAdded;
26 26 vm.clearFile = clearFile;
27 27
  28 + vm.addDevices = addDevices;
  29 + vm.importParams = {
  30 + isUpdate: true
  31 + };
  32 +
28 33 vm.importTitle = importTitle;
29 34 vm.importFileLabel = importFileLabel;
30 35
  36 + vm.columnsParam = [];
  37 +
  38 + vm.entityType = types.entityType.device;
  39 + vm.columnTypes = {};
  40 + vm.entityField = {};
  41 +
  42 + var parseData = {};
  43 +
  44 + switch (vm.entityType) {
  45 + case types.entityType.device:
  46 + vm.columnTypes = types.entityGroup.columnType;
  47 + break;
  48 + }
  49 +
  50 + vm.entityField.name = types.entityGroup.entityField.name;
  51 +
  52 + switch (vm.entityType) {
  53 + case types.entityType.device:
  54 + vm.entityField.type = types.entityGroup.entityField.type;
  55 + // vm.entityField.assigned_customer = types.entityGroup.entityField.assigned_customer;
  56 + break;
  57 + }
  58 +
  59 + $scope.$watch('vm.columnsParam', function(newVal, prevVal){
  60 + if (newVal && !angular.equals(newVal, prevVal)) {
  61 + var isSelectName = false;
  62 + var isSelectType = false;
  63 + for (var i = 0; i < newVal.length; i++) {
  64 + if (newVal[i].type === types.entityGroup.columnType.entityField.value &&
  65 + newVal[i].key === types.entityGroup.entityField.name.value) {
  66 + isSelectName = true;
  67 + }
  68 + if (newVal[i].type === types.entityGroup.columnType.entityField.value &&
  69 + newVal[i].key === types.entityGroup.entityField.type.value) {
  70 + isSelectType = true;
  71 + }
  72 + }
  73 + $timeout(function () {
  74 + vm.entityField.name.disable = isSelectName;
  75 + vm.entityField.type.disable = isSelectType;
  76 + });
  77 + }
  78 + }, true);
  79 +
31 80
32 81 function cancel() {
33 82 $mdDialog.cancel();
... ... @@ -36,16 +85,16 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
36 85 function fileAdded($file) {
37 86 if ($file.getExtension() === 'csv') {
38 87 var reader = new FileReader();
39   - reader.onload = function(event) {
40   - $scope.$apply(function() {
  88 + reader.onload = function (event) {
  89 + $scope.$apply(function () {
41 90 if (event.target.result) {
42 91 $scope.theForm.$setDirty();
43 92 var importCSV = event.target.result;
44 93 if (importCSV && importCSV.length > 0) {
45 94 try {
46 95 vm.importData = importCSV;
47   - console.log(vm.importData); // eslint-disable-line
48 96 vm.fileName = $file.name;
  97 + parseCSVData(vm.importData);
49 98 } catch (err) {
50 99 vm.fileName = null;
51 100 toast.showError(err.message);
... ... @@ -58,10 +107,89 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo
58 107 }
59 108 }
60 109
  110 + function parseCSVData(importData) {
  111 + var config = {
  112 + delim: vm.importParams.delim,
  113 + header: vm.importParams.isHeader
  114 + };
  115 + parseData = importExport.convertCSVToJson(importData, config);
  116 + for (var i = 0; i < parseData.headers.length; i++) {
  117 + var columnParam = {
  118 + type: types.entityGroup.columnType.serverAttribute.value,
  119 + key: vm.importParams.isHeader ? parseData.headers[i] : "",
  120 + sampleData: parseData.rows[0][i]
  121 + };
  122 + vm.columnsParam.push(columnParam);
  123 + }
  124 + }
  125 +
  126 + function addDevices () {
  127 + var arrayParam = [{type: "ENTITY_FIELD", key: "name", sampleData: "Device 1"}, {type: "ENTITY_FIELD", key: "type", sampleData: "test"}, {type: "SERVER_ATTRIBUTE", key: "test", sampleData: "test"}, {type: "TIMESERIES", key: "testBoolean", sampleData: false}, {type: "SHARED_ATTRIBUTE", key: "testNumber", sampleData: 123}]; // eslint-disable-line
  128 + var data = {headers: ["Device 1", "test", "test", "FALSE", "123"],
  129 + rows:[["Device 1", "test", "test", false, 123.5]]};
  130 + // rows:[["Device 1", "test", "test", false, 123],
  131 + // ["Device 2", "test", "test", false, 124],
  132 + // ["Device 3", "test", "test", false, 125],
  133 + // ["Device 4", "test", "test", false, 126],
  134 + // ["Device 5", "test", "test", false, 127]]};
  135 + var arrayData = [];
  136 + var config = {
  137 + ignoreErrors: true
  138 + };
  139 + for (var i = 0; i < data.rows.length; i ++) {
  140 + var obj = {
  141 + name: "",
  142 + type: "",
  143 + attributes: {
  144 + server: [],
  145 + shared: []
  146 + },
  147 + timeseries: []
  148 + };
  149 + for(var j = 0; j < arrayParam.length; j++){
  150 + switch (arrayParam[j].type) {
  151 + case types.entityGroup.columnType.serverAttribute.value:
  152 + obj.attributes.server.push({
  153 + key: arrayParam[j].key,
  154 + value: data.rows[i][j]
  155 + });
  156 + break;
  157 + case types.entityGroup.columnType.sharedAttribute.value:
  158 + obj.attributes.shared.push({
  159 + key: arrayParam[j].key,
  160 + value: data.rows[i][j]
  161 + });
  162 + break;
  163 + case types.entityGroup.columnType.timeseries.value:
  164 + obj.timeseries.push({
  165 + key: arrayParam[j].key,
  166 + value: data.rows[i][j]
  167 + });
  168 + break;
  169 + case types.entityGroup.columnType.entityField.value:
  170 + switch (arrayParam[j].key) {
  171 + case types.entityGroup.entityField.name.value:
  172 + obj.name = data.rows[i][j];
  173 + break;
  174 + case types.entityGroup.entityField.type.value:
  175 + obj.type = data.rows[i][j];
  176 + break;
  177 + }
  178 + break;
  179 + }
  180 + }
  181 + arrayData.push(obj);
  182 + }
  183 + importExport.createMultiEntity(arrayData, vm.entityType, vm.importParams.isUpdate, config).then(function () {
  184 + $mdDialog.hide();
  185 + });
  186 + }
  187 +
61 188 function clearFile() {
62 189 $scope.theForm.$setDirty();
63 190 vm.fileName = null;
64 191 vm.importData = null;
  192 + vm.columnsParam = [];
65 193 }
66 194
67 195 function importFromJson() {
... ...
... ... @@ -26,7 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading"
  30 + ng-show="$root.loading"></md-progress-linear>
30 31 <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 32 <md-dialog-content>
32 33 <div class="md-dialog-content">
... ... @@ -38,7 +39,8 @@
38 39 flow-file-added="vm.fileAdded( $file )" class="tb-file-select-container">
39 40 <div class="tb-file-clear-container">
40 41 <md-button ng-click="vm.clearFile()"
41   - class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}">
  42 + class="tb-file-clear-btn md-icon-button md-primary"
  43 + aria-label="{{ 'action.remove' | translate }}">
42 44 <md-tooltip md-direction="top">
43 45 {{ 'action.remove' | translate }}
44 46 </md-tooltip>
... ... @@ -49,7 +51,8 @@
49 51 </div>
50 52 <div class="alert tb-flow-drop" flow-drop>
51 53 <label for="select" translate>import.drop-file-csv</label>
52   - <input class="file-input" flow-btn flow-attrs="{accept:'.csv,application/csv,text/csv'}" id="select">
  54 + <input class="file-input" flow-btn
  55 + flow-attrs="{accept:'.csv,application/csv,text/csv'}" id="select">
53 56 </div>
54 57 </div>
55 58 </div>
... ... @@ -59,14 +62,81 @@
59 62 </div>
60 63 </div>
61 64 </fieldset>
  65 + <div flex layout="row">
  66 + <md-input-container class="md-block">
  67 + <label translate>CSV delimiter parametr</label>
  68 + <input ng-model="vm.importParams.delim">
  69 + </md-input-container>
  70 + <md-input-container class="md-block">
  71 + <md-checkbox ng-model="vm.importParams.isHeader" aria-label="Checkbox 1">
  72 + Use first line is header
  73 + </md-checkbox>
  74 + </md-input-container>
  75 + <md-input-container class="md-block">
  76 + <md-checkbox ng-model="vm.importParams.isUpdate" aria-label="Checkbox 1">
  77 + Update parameter device
  78 + </md-checkbox>
  79 + </md-input-container>
  80 + </div>
  81 + <md-table-container flex class="tb-table-select">
  82 + <table md-table>
  83 + <thead md-head>
  84 + <tr md-row>
  85 + <th md-column>&nbsp</th>
  86 + <th md-column>Example value data</th>
  87 + <th md-column style="min-width: 140px">Column type</th>
  88 + <th md-column style="min-width: 140px">Value</th>
  89 + </tr>
  90 + </thead>
  91 + <tbody md-body>
  92 + <tr md-row ng-repeat="column in vm.columnsParam">
  93 + <td md-cell>{{$index + 1}}</td>
  94 + <td md-cell>{{column.sampleData}}</td>
  95 + <td md-cell>
  96 + <md-select ng-model="column.type" required name="columnType"
  97 + aria-label="{{ 'entity-group.column-type' | translate }}">
  98 + <md-option ng-repeat="type in vm.columnTypes" ng-value="type.value">
  99 + {{type.name | translate}}
  100 + </md-option>
  101 + </md-select>
  102 + </td>
  103 + <td md-cell>
  104 + <md-select ng-if="column.type == vm.columnTypes.entityField.value"
  105 + required name="columnKey" ng-model="column.key"
  106 + aria-label="{{ 'entity-group.column-value' | translate }}">
  107 + <md-option ng-repeat="field in vm.entityField" ng-value="field.value" ng-disabled="field.disable">
  108 + {{field.name | translate}}
  109 + </md-option>
  110 + </md-select>
  111 + <md-input-container md-no-float
  112 + ng-if="column.type != vm.columnTypes.entityField.value &&
  113 + column.type != vm.columnTypes.name.value &&
  114 + column.type != vm.columnTypes.type.value">
  115 + <input required name="columnKeyName"
  116 + placeholder="{{ 'entity-group.column-value' | translate }}"
  117 + ng-model="column.key"
  118 + aria-label="{{ 'entity-group.column-value' | translate }}">
  119 + </md-input-container>
  120 + </td>
  121 + </tr>
  122 + </tbody>
  123 + </table>
  124 + <md-divider></md-divider>
  125 + </md-table-container>
62 126 </div>
63 127 </md-dialog-content>
64 128 <md-dialog-actions layout="row">
65 129 <span flex></span>
66   - <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit" class="md-raised md-primary">
  130 + <md-button ng-disabled="$root.loading" ng-click="vm.addDevices()" style="margin-right:20px;">
  131 + Add Device
  132 + </md-button>
  133 + <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit"
  134 + class="md-raised md-primary">
67 135 {{ 'action.import' | translate }}
68 136 </md-button>
69   - <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  137 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel'
  138 + | translate }}
  139 + </md-button>
70 140 </md-dialog-actions>
71 141 </form>
72 142 </md-dialog>
... ...
... ... @@ -40,3 +40,13 @@ $previewSize: 100px !default;
40 40 top: 50%;
41 41 transform: translate(0%, -50%) !important;
42 42 }
  43 +
  44 +.tb-table-select{
  45 + md-input-container{
  46 + margin: 0;
  47 +
  48 + .md-errors-spacer{
  49 + min-height: 0;
  50 + }
  51 + }
  52 +}
... ...
... ... @@ -25,7 +25,7 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html';
25 25 /* eslint-disable no-undef, angular/window-service, angular/document-service */
26 26
27 27 /*@ngInject*/
28   -export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types,
  28 +export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, $timeout, deviceService,
29 29 dashboardUtils, entityService, dashboardService, ruleChainService, widgetService, toast, attributeService) {
30 30
31 31
... ... @@ -43,7 +43,9 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
43 43 exportExtension: exportExtension,
44 44 importExtension: importExtension,
45 45 importDevices: importDevices,
46   - exportToPc: exportToPc
  46 + convertCSVToJson: convertCSVToJson,
  47 + exportToPc: exportToPc,
  48 + createMultiEntity: createMultiEntity
47 49 };
48 50
49 51 return service;
... ... @@ -56,11 +58,11 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
56 58 var bundleAlias = widgetsBundle.alias;
57 59 var isSystem = widgetsBundle.tenantId.id === types.id.nullUid;
58 60 widgetService.getBundleWidgetTypes(bundleAlias, isSystem).then(
59   - function success (widgetTypes) {
  61 + function success(widgetTypes) {
60 62 prepareExport(widgetsBundle);
61 63 var widgetsBundleItem = {
62   - widgetsBundle: prepareExport(widgetsBundle),
63   - widgetTypes: []
  64 + widgetsBundle: prepareExport(widgetsBundle),
  65 + widgetTypes: []
64 66 };
65 67 for (var t in widgetTypes) {
66 68 var widgetType = widgetTypes[t];
... ... @@ -70,10 +72,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
70 72 widgetsBundleItem.widgetTypes.push(prepareExport(widgetType));
71 73 }
72 74 var name = widgetsBundle.title;
73   - name = name.toLowerCase().replace(/\W/g,"_");
  75 + name = name.toLowerCase().replace(/\W/g, "_");
74 76 exportToPc(widgetsBundleItem, name + '.json');
75 77 },
76   - function fail (rejection) {
  78 + function fail(rejection) {
77 79 var message = rejection;
78 80 if (!message) {
79 81 message = $translate.instant('error.unknown-error');
... ... @@ -170,7 +172,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
170 172 delete widgetType.bundleAlias;
171 173 }
172 174 var name = widgetType.name;
173   - name = name.toLowerCase().replace(/\W/g,"_");
  175 + name = name.toLowerCase().replace(/\W/g, "_");
174 176 exportToPc(prepareExport(widgetType), name + '.json');
175 177 },
176 178 function fail(rejection) {
... ... @@ -211,8 +213,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
211 213
212 214 function validateImportedWidgetType(widgetType) {
213 215 if (angular.isUndefined(widgetType.name)
214   - || angular.isUndefined(widgetType.descriptor))
215   - {
  216 + || angular.isUndefined(widgetType.descriptor)) {
216 217 return false;
217 218 }
218 219 return true;
... ... @@ -230,7 +231,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
230 231 metadata: prepareRuleChainMetaData(ruleChainMetaData)
231 232 };
232 233 var name = ruleChain.name;
233   - name = name.toLowerCase().replace(/\W/g,"_");
  234 + name = name.toLowerCase().replace(/\W/g, "_");
234 235 exportToPc(ruleChainExport, name + '.json');
235 236 },
236 237 (rejection) => {
... ... @@ -255,7 +256,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
255 256
256 257 function prepareRuleChainMetaData(ruleChainMetaData) {
257 258 delete ruleChainMetaData.ruleChainId;
258   - for (var i=0;i<ruleChainMetaData.nodes.length;i++) {
  259 + for (var i = 0; i < ruleChainMetaData.nodes.length; i++) {
259 260 var node = ruleChainMetaData.nodes[i];
260 261 delete node.ruleChainId;
261 262 ruleChainMetaData.nodes[i] = prepareExport(node);
... ... @@ -307,7 +308,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
307 308 function exportWidget(dashboard, sourceState, sourceLayout, widget) {
308 309 var widgetItem = itembuffer.prepareWidgetItem(dashboard, sourceState, sourceLayout, widget);
309 310 var name = widgetItem.widget.config.title;
310   - name = name.toLowerCase().replace(/\W/g,"_");
  311 + name = name.toLowerCase().replace(/\W/g, "_");
311 312 exportToPc(prepareExport(widgetItem), name + '.json');
312 313 }
313 314
... ... @@ -417,10 +418,10 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
417 418 var aliasIds = Object.keys(entityAliases);
418 419 if (aliasIds.length > 0) {
419 420 processEntityAliases(entityAliases, aliasIds).then(
420   - function(missingEntityAliases) {
  421 + function (missingEntityAliases) {
421 422 if (Object.keys(missingEntityAliases).length > 0) {
422   - editMissingAliases($event, [ widget ],
423   - true, 'dashboard.widget-import-missing-aliases-title', missingEntityAliases).then(
  423 + editMissingAliases($event, [widget],
  424 + true, 'dashboard.widget-import-missing-aliases-title', missingEntityAliases).then(
424 425 function success(updatedEntityAliases) {
425 426 for (var aliasId in updatedEntityAliases) {
426 427 var entityAlias = updatedEntityAliases[aliasId];
... ... @@ -485,14 +486,14 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
485 486 function success(targetLayout) {
486 487 itembuffer.addWidgetToDashboard(dashboard, targetState, targetLayout, widget,
487 488 aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, -1, -1).then(
488   - function() {
489   - deferred.resolve(
490   - {
491   - widget: widget,
492   - layoutId: targetLayout
493   - }
494   - );
495   - }
  489 + function () {
  490 + deferred.resolve(
  491 + {
  492 + widget: widget,
  493 + layoutId: targetLayout
  494 + }
  495 + );
  496 + }
496 497 );
497 498 },
498 499 function fail() {
... ... @@ -507,7 +508,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
507 508 dashboardService.getDashboard(dashboardId).then(
508 509 function success(dashboard) {
509 510 var name = dashboard.title;
510   - name = name.toLowerCase().replace(/\W/g,"_");
  511 + name = name.toLowerCase().replace(/\W/g, "_");
511 512 exportToPc(prepareDashboardExport(dashboard), name + '.json');
512 513 },
513 514 function fail(rejection) {
... ... @@ -540,13 +541,13 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
540 541 dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard);
541 542 var entityAliases = dashboard.configuration.entityAliases;
542 543 if (entityAliases) {
543   - var aliasIds = Object.keys( entityAliases );
  544 + var aliasIds = Object.keys(entityAliases);
544 545 if (aliasIds.length > 0) {
545 546 processEntityAliases(entityAliases, aliasIds).then(
546   - function(missingEntityAliases) {
547   - if (Object.keys( missingEntityAliases ).length > 0) {
  547 + function (missingEntityAliases) {
  548 + if (Object.keys(missingEntityAliases).length > 0) {
548 549 editMissingAliases($event, dashboard.configuration.widgets,
549   - false, 'dashboard.dashboard-import-missing-aliases-title', missingEntityAliases).then(
  550 + false, 'dashboard.dashboard-import-missing-aliases-title', missingEntityAliases).then(
550 551 function success(updatedEntityAliases) {
551 552 for (var aliasId in updatedEntityAliases) {
552 553 entityAliases[aliasId] = updatedEntityAliases[aliasId];
... ... @@ -617,6 +618,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
617 618 // saveImportedDashboard(dashboard, deferred);
618 619 // }
619 620 // }
  621 + deferred.resolve();
620 622 },
621 623 function fail() {
622 624 deferred.reject();
... ... @@ -644,14 +646,13 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
644 646 }
645 647
646 648
647   -
648 649 function exportExtension(extensionId) {
649 650
650 651 getExtension(extensionId)
651 652 .then(
652 653 function success(extension) {
653 654 var name = extension.title;
654   - name = name.toLowerCase().replace(/\W/g,"_");
  655 + name = name.toLowerCase().replace(/\W/g, "_");
655 656 exportToPc(prepareExport(extension), name + '.json');
656 657 },
657 658 function fail(rejection) {
... ... @@ -711,7 +712,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
711 712 function validateImportedExtension(configuration) {
712 713 if (configuration.length) {
713 714 for (let i = 0; i < configuration.length; i++) {
714   - if (angular.isUndefined(configuration[i].configuration) || angular.isUndefined(configuration[i].id )|| angular.isUndefined(configuration[i].type)) {
  715 + if (angular.isUndefined(configuration[i].configuration) || angular.isUndefined(configuration[i].id) || angular.isUndefined(configuration[i].type)) {
715 716 return false;
716 717 }
717 718 }
... ... @@ -742,7 +743,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
742 743 var aliasId = aliasIds[index];
743 744 var entityAlias = entityAliases[aliasId];
744 745 entityService.checkEntityAlias(entityAlias).then(
745   - function(result) {
  746 + function (result) {
746 747 if (result) {
747 748 checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred);
748 749 } else {
... ... @@ -782,6 +783,91 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
782 783 return deferred.promise;
783 784 }
784 785
  786 + /**
  787 + * splitCSV function (c) 2009 Brian Huisman, see http://www.greywyvern.com/?post=258
  788 + * Works by spliting on seperators first, then patching together quoted values
  789 + */
  790 + function splitCSV(str, sep) {
  791 + for (var foo = str.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
  792 + if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') {
  793 + if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') {
  794 + foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
  795 + } else if (x) {
  796 + foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
  797 + } else foo = foo.shift().split(sep).concat(foo);
  798 + } else foo[x].replace(/""/g, '"');
  799 + }
  800 + return foo;
  801 + }
  802 +
  803 + function isNumeric(str) {
  804 + str = str.replace(',', '.');
  805 + return !isNaN(parseFloat(str)) && isFinite(str);
  806 + }
  807 +
  808 + function parseStringToFormatJS(str) {
  809 + if (isNumeric(str.replace(',', '.'))) {
  810 + return parseFloat(str.replace(',', '.'));
  811 + }
  812 + if (str.search(/^(true|false)$/im) === 0) {
  813 + return str.toLowerCase() === 'true';
  814 + }
  815 + if (str === "") {
  816 + return null;
  817 + }
  818 + return str;
  819 + }
  820 +
  821 + function convertCSVToJson(csvdata, config) {
  822 + config = config || {};
  823 + const delim = config.delim || ",";
  824 + const header = config.header || false;
  825 +
  826 + let csvlines = csvdata.split(/[\r\n]+/);
  827 + let csvheaders = splitCSV(csvlines[0], delim);
  828 + let csvrows = header ? csvlines.slice(1, csvlines.length) : csvlines;
  829 +
  830 + let result = {};
  831 + result.headers = csvheaders;
  832 + result.rows = [];
  833 +
  834 + for (let r in csvrows) {
  835 + if (csvrows.hasOwnProperty(r)) {
  836 + let row = csvrows[r];
  837 +
  838 + if (row.length === 0)
  839 + break;
  840 +
  841 + let rowitems = splitCSV(row, delim);
  842 + for (let i = 0; i < rowitems.length; i++) {
  843 + rowitems[i] = parseStringToFormatJS(rowitems[i]);
  844 + }
  845 + result.rows.push(rowitems);
  846 + }
  847 + }
  848 + return result;
  849 + }
  850 +
  851 + function createMultiEntity(arrayData, entityType, update, config) {
  852 + var deferred = $q.defer();
  853 + var allPromise = [];
  854 + switch (entityType) {
  855 + case types.entityType.device:
  856 + for(var i = 0; i < arrayData.length; i++){
  857 + var promise = deviceService.saveDeviceParameters(arrayData[i], update, config);
  858 + allPromise.push(promise);
  859 + }
  860 + break;
  861 + }
  862 + $q.all(allPromise).then(function success() {
  863 + deferred.resolve();
  864 + $timeout(function () {
  865 + console.log("1"); // eslint-disable-line
  866 + }, 1000);
  867 + });
  868 + return deferred.promise;
  869 + }
  870 +
785 871 // Common functions
786 872
787 873 function prepareExport(data) {
... ... @@ -821,8 +907,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
821 907
822 908 if (window.navigator && window.navigator.msSaveOrOpenBlob) {
823 909 window.navigator.msSaveOrOpenBlob(blob, filename);
824   - }
825   - else{
  910 + } else {
826 911 var e = document.createEvent('MouseEvents'),
827 912 a = document.createElement('a');
828 913
... ... @@ -873,7 +958,6 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
873 958 targetEvent: $event
874 959 }).then(function (importData) {
875 960 deferred.resolve(importData);
876   -
877 961 }, function () {
878 962 deferred.reject();
879 963 });
... ...
... ... @@ -778,6 +778,22 @@
778 778 "no-data": "No data to display",
779 779 "columns-to-display": "Columns to Display"
780 780 },
  781 + "entity-group": {
  782 + "column-value": "Value",
  783 + "column-title": "Title",
  784 + "column-type": {
  785 + "column-type": "Column type",
  786 + "shared-attribute": "Shared attribute",
  787 + "server-attribute": "Server attribute",
  788 + "timeseries": "Timeseries",
  789 + "entity-field": "Entity field"
  790 + },
  791 + "entity-field": {
  792 + "name": "Name",
  793 + "type": "Type",
  794 + "assigned_customer": "Assigned Customer"
  795 + }
  796 + },
781 797 "entity-view": {
782 798 "entity-view": "Entity View",
783 799 "entity-view-required": "Entity view is required.",
... ...