Commit b3abfd38666a0b2cc6900a7791d34ee692514786

Authored by Vladyslav
Committed by GitHub
1 parent 320b9911

Refactoring gateway configuration form (#2389)

* Change translate, layout and clear code

* Refactoring code

* Refactoring code

* Refactoring code

* Code refactoring
... ... @@ -22,23 +22,19 @@
22 22 }
23 23 },
24 24 {
25   - "alias": "new_config_form",
26   - "name": "Config form",
  25 + "alias": "gateway_configuration",
  26 + "name": "Gateway Configuration",
27 27 "descriptor": {
28 28 "type": "static",
29   - "sizeX": 7.5,
30   - "sizeY": 10.5,
31   - "resources": [
32   - {
33   - "url": ""
34   - }
35   - ],
  29 + "sizeX": 8,
  30 + "sizeY": 6.5,
  31 + "resources": [],
36 32 "templateHtml": "<tb-gateway-form\n form-id=\"formId\"\n ctx=\"ctx\">\n</tb-gateway-form>\n",
37   - "templateCss": "#container {\n overflow: auto;\n height: 100%;\n margin: auto;\n}\n\n\n\n/*#configurations {*/\n/* display: flex;*/\n/* flex-direction: column;*/\n/* height: 100%;*/\n/* margin: 0px;*/\n/* padding: 0;*/\n/*}*/\n\n/*.configurationPointParent {*/\n/* display: flex;*/\n/* flex-direction: column;*/\n \n/*}*/\n\n/*.configurationPoint {*/\n/* display: flex;*/\n/* flex-direction: row;*/\n/* justify-content: space-between;*/\n/* margin: 5px;*/\n/*}*/\n\n/*.configurationPoint.select {*/\n/* margin: 0px;*/\n/* padding: 0;*/\n/* border: 0;*/\n/* height: 40px;*/\n\n/*}*/\n\n/*.configurationPoint.select.inputRow {*/\n/* margin: 0px;*/\n/* width: 100%;*/\n/* padding: 0;*/\n/* border: 0;*/\n/* height: 40px;*/\n/*}*/\n\n\n/*.error {*/\n/*color: red;*/\n/*}*/",
  33 + "templateCss": "",
38 34 "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.formId = \"form-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n self.ctx.$scope.$broadcast('gateway-form-resize', self.ctx.$scope.formId);\n}\n",
39   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"GatewayConfigForm\",\n \"properties\": {\n \"gatewayTitle\": {\n \"title\": \"Gateway form title\",\n \"type\": \"string\",\n \"default\": \"Gateway Config Form\"\n }\n }\n },\n \"form\": [\n \"gatewayTitle\"\n ]\n}\n",
  35 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"Gatwey Configuration\"\n },\n \"archiveFileName\": {\n \"title\": \"Default archive file name\",\n \"type\": \"string\",\n \"default\": \"gatewayConfiguration\"\n },\n \"gatewayType\": {\n \"title\": \"Device type for new gateway\",\n \"type\": \"string\",\n \"default\": \"Gateway\"\n },\n \"successfulSave\": {\n \"title\": \"Text message about successfully saved gateway configuration\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"gatewayNameExists\": {\n \"title\": \"Text message when device with entered name is already exists\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n [\n \"widgetTitle\",\n \"archiveFileName\",\n \"gatewayType\"\n ],\n [\n \"successfulSave\",\n \"gatewayNameExists\"\n ]\n ],\n \"groupInfoes\": [{\n \"formIndex\": 0,\n \"GroupTitle\": \"General settings\"\n }, {\n \"formIndex\": 1,\n \"GroupTitle\": \"Messages settings\"\n }]\n}",
40 36 "dataKeySettingsSchema": "{}\n",
41   - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"gatewayTitle\":\"Gateway Config Form\"},\"title\":\"Config form\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
  37 + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetTitle\":\"Gatwey Configuration\",\"archiveFileName\":\"configurationGateway\"},\"title\":\"Gateway Configuration\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
42 38 }
43 39 }
44 40 ]
... ...
1   -/*
2   - * Copyright © 2016-2020 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -import './gateway-config.scss';
17   -
18   -/* eslint-disable import/no-unresolved, import/default */
19   -
20   -import gatewayTemplate from './gateway-config.tpl.html';
21   -import gatewayDialogTemplate from './gateway-config-dialog.tpl.html';
22   -import beautify from "js-beautify";
23   -
24   -/* eslint-enable import/no-unresolved, import/default */
25   -const js_beautify = beautify.js;
26   -
27   -export default angular.module('thingsboard.directives.gatewayConfig', [])
28   - .directive('tbGatewayConfig', GatewayConfig)
29   - .name;
30   -
31   -/*@ngInject*/
32   -function GatewayConfig() {
33   - return {
34   - restrict: "E",
35   - scope: true,
36   - bindToController: {
37   - disabled: '=ngDisabled',
38   - titleText: '@?',
39   - keyPlaceholderText: '@?',
40   - valuePlaceholderText: '@?',
41   - noDataText: '@?',
42   - gatewayConfig: '=',
43   - changeAlignment: '='
44   - },
45   - controller: GatewayConfigController,
46   - controllerAs: 'vm',
47   - templateUrl: gatewayTemplate
48   - };
49   -}
50   -
51   -/*@ngInject*/
52   -function GatewayConfigController($scope, $document, $mdDialog, $mdUtil, $window, types, toast, $timeout, $compile, $translate) { //eslint-disable-line
53   -
54   - let vm = this;
55   -
56   - vm.kvList = [];
57   - vm.types = types;
58   - $scope.$watch('vm.gatewayConfig', () => {
59   - vm.stopWatchKvList();
60   - vm.kvList.length = 0;
61   - if (vm.gatewayConfig) {
62   - for (var property in vm.gatewayConfig) {
63   - if (Object.prototype.hasOwnProperty.call(vm.gatewayConfig, property)) {
64   - vm.kvList.push(
65   - {
66   - enabled: vm.gatewayConfig[property].enabled,
67   - key: property + '',
68   - value: vm.gatewayConfig[property].connector + '',
69   - config: js_beautify(vm.gatewayConfig[property].config + '', {indent_size: 4})
70   - }
71   - );
72   - }
73   - }
74   - }
75   - $mdUtil.nextTick(() => {
76   - vm.watchKvList();
77   - });
78   - });
79   -
80   - vm.watchKvList = () => {
81   - $scope.kvListWatcher = $scope.$watch('vm.kvList', () => {
82   - if (!vm.gatewayConfig) {
83   - return;
84   - }
85   - for (let property in vm.gatewayConfig) {
86   - if (Object.prototype.hasOwnProperty.call(vm.gatewayConfig, property)) {
87   - delete vm.gatewayConfig[property];
88   - }
89   - }
90   - for (let i = 0; i < vm.kvList.length; i++) {
91   - let entry = vm.kvList[i];
92   - if (entry.key && entry.value) {
93   - let connectorJSON = angular.toJson({
94   - enabled: entry.enabled,
95   - connector: entry.value,
96   - config: angular.fromJson(entry.config)
97   - });
98   - vm.gatewayConfig [entry.key] = angular.fromJson(connectorJSON);
99   - }
100   - }
101   - }, true);
102   - };
103   -
104   - vm.stopWatchKvList = () => {
105   - if ($scope.kvListWatcher) {
106   - $scope.kvListWatcher();
107   - $scope.kvListWatcher = null;
108   - }
109   - };
110   -
111   - vm.removeKeyVal = (index) => {
112   - if (index > -1) {
113   - vm.kvList.splice(index, 1);
114   - }
115   - };
116   -
117   - vm.addKeyVal = () => {
118   - if (!vm.kvList) {
119   - vm.kvList = [];
120   - }
121   - vm.kvList.push(
122   - {
123   - enabled: false,
124   - key: '',
125   - value: '',
126   - config: '{}'
127   - }
128   - );
129   - }
130   -
131   - vm.openConfigDialog = ($event, index, config, typeName) => {
132   - if ($event) {
133   - $event.stopPropagation();
134   - }
135   - $mdDialog.show({
136   - controller: GatewayDialogController,
137   - controllerAs: 'vm',
138   - templateUrl: gatewayDialogTemplate,
139   - parent: angular.element($document[0].body),
140   - locals: {
141   - config: config,
142   - typeName: typeName
143   - },
144   - targetEvent: $event,
145   - fullscreen: true,
146   - multiple: true,
147   - }).then(function (config) {
148   - if (config) {
149   - if (index > -1) {
150   - vm.kvList[index].config = config;
151   - }
152   - }
153   - }, function () {
154   - });
155   -
156   - };
157   -
158   - vm.configTypeChange = (keyVal) => {
159   - for (let prop in types.gatewayConfigType) {
160   - if (types.gatewayConfigType[prop].value === keyVal.value) {
161   - if (!keyVal.key) {
162   - keyVal.key = vm.configTypeChangeValid(types.gatewayConfigType[prop].name, 0);
163   - }
164   - }
165   - }
166   - vm.checkboxValid(keyVal);
167   - };
168   -
169   - vm.keyValChange = (keyVal, indexKey) => {
170   - keyVal.key = vm.keyValChangeValid(keyVal.key, 0, indexKey);
171   - vm.checkboxValid(keyVal);
172   - };
173   -
174   - vm.configTypeChangeValid = (name, index) => {
175   - let newKeyName = index ? name + index : name;
176   - let indexRes = vm.kvList.findIndex((element) => element.key === newKeyName);
177   - return indexRes === -1 ? newKeyName : vm.configTypeChangeValid(name, ++index);
178   - };
179   -
180   - vm.keyValChangeValid = (name, index, indexKey) => {
181   - angular.forEach(vm.kvList, function (value, key) {
182   - let nameEq = (index === 0) ? name : name + index;
183   - if (key !== indexKey && value.key && value.key === nameEq) {
184   - index++;
185   - vm.keyValChangeValid(name, index, indexKey);
186   - }
187   -
188   - });
189   - return (index === 0) ? name : name + index;
190   - };
191   -
192   - vm.buttonValid = (config) => {
193   - return (angular.equals("{}", config)) ? "md-warn" : "md-primary";
194   - };
195   -
196   - vm.checkboxValid = (keyVal) => {
197   - if (!keyVal.key || angular.equals("", keyVal.key)
198   - || !keyVal.value || angular.equals("", keyVal.value)
199   - || angular.equals("{}", keyVal.config)) {
200   - return keyVal.enabled = false;
201   - }
202   - return true;
203   - };
204   - vm.checkboxValidMouseover = ($event, keyVal) => {
205   - console.log($event, keyVal); //eslint-disable-line
206   - vm.checkboxValidClick ($event, keyVal);
207   - };
208   -
209   - vm.checkboxValidClick = ($event, keyVal) => {
210   - if (!vm.checkboxValid(keyVal)) {
211   - let errTxt = "";
212   - if (!keyVal.key || angular.equals("", keyVal.key)) {
213   - errTxt = $translate.instant('gateway.keyval-name-err');
214   - }
215   -
216   - if (!keyVal.value || angular.equals("", keyVal.value)) {
217   - errTxt += '<div>' + $translate.instant('gateway.keyval-type-err') + '</div>';
218   - }
219   -
220   - if (angular.equals("{}", keyVal.config)) {
221   - errTxt += '<div>' + $translate.instant('gateway.keyval-config-err') + '</div>';
222   - }
223   - if (!angular.equals("", errTxt)) {
224   - displayTooltip($event, '<div class="tb-rule-node-tooltip tb-lib-tooltip">' +
225   - '<div id="tb-node-content" layout="column">' +
226   - '<div class="tb-node-title">' + $translate.instant('gateway.keyval-save-err') + '</div>' +
227   - '<div class="tb-node-details">' + errTxt + '</div>' +
228   - '</div>' +
229   - '</div>');
230   - }
231   - }
232   - else {
233   - destroyTooltips();
234   - }
235   - };
236   -
237   -
238   - function displayTooltip(event, content) {
239   - destroyTooltips();
240   - vm.tooltipTimeout = $timeout(() => {
241   - var element = angular.element(event.target);
242   - element.tooltipster(
243   - {
244   - theme: 'tooltipster-shadow',
245   - delay: 10,
246   - animation: 'grow',
247   - side: 'right'
248   - }
249   - );
250   - var contentElement = angular.element(content);
251   - $compile(contentElement)($scope);
252   - var tooltip = element.tooltipster('instance');
253   - tooltip.content(contentElement);
254   - tooltip.open();
255   - }, 500);
256   - }
257   -
258   - function destroyTooltips() {
259   - if (vm.tooltipTimeout) {
260   - $timeout.cancel(vm.tooltipTimeout);
261   - vm.tooltipTimeout = null;
262   - }
263   - var instances = angular.element.tooltipster.instances();
264   - instances.forEach((instance) => {
265   - if (!instance.isErrorTooltip) {
266   - instance.destroy();
267   - }
268   - });
269   - }
270   -}
271   -
272   -/*@ngInject*/
273   -function GatewayDialogController($scope, $mdDialog, $document, $window, config, typeName) {
274   - let vm = this;
275   - vm.doc = $document[0];
276   - vm.config = angular.copy(config);
277   - vm.typeName = "" + typeName;
278   - vm.configAreaOptions = {
279   - useWrapMode: false,
280   - mode: 'json',
281   - showGutter: true,
282   - showPrintMargin: true,
283   - theme: 'github',
284   - advanced: {
285   - enableSnippets: true,
286   - enableBasicAutocompletion: true,
287   - enableLiveAutocompletion: true
288   - },
289   - onLoad: function (_ace) {
290   - _ace.$blockScrolling = 1;
291   - }
292   - };
293   -
294   - vm.validateConfig = (model, editorName) => {
295   - if (model && model.length) {
296   - try {
297   - angular.fromJson(model);
298   - $scope.theForm[editorName].$setValidity('configJSON', true);
299   - } catch (e) {
300   - $scope.theForm[editorName].$setValidity('configJSON', false);
301   - }
302   - }
303   - };
304   -
305   - vm.save = () => {
306   - $mdDialog.hide(vm.config);
307   - };
308   -
309   - vm.cancel = () => {
310   - $mdDialog.hide();
311   - };
312   -
313   - vm.beautifyJson = () => {
314   - vm.config = js_beautify(vm.config, {indent_size: 4});
315   - };
316   -}
317   -
1   -/*
2   - * Copyright © 2016-2020 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -import './gateway-form.scss';
17   -/* eslint-disable import/no-unresolved, import/default */
18   -
19   -import gatewayFormTemplate from './gateway-form.tpl.html';
20   -
21   -/* eslint-enable import/no-unresolved, import/default */
22   -
23   -export default angular.module('thingsboard.directives.gatewayForm', [])
24   - .directive('tbGatewayForm', GatewayForm)
25   - .name;
26   -
27   -/*@ngInject*/
28   -function GatewayForm() {
29   - return {
30   - restrict: "E",
31   - scope: true,
32   - bindToController: {
33   - disabled: '=ngDisabled',
34   - keyPlaceholderText: '@?',
35   - valuePlaceholderText: '@?',
36   - noDataText: '@?',
37   - formId: '=',
38   - ctx: '=',
39   - gatewayFormConfig: '=',
40   - theForm: '='
41   - },
42   - controller: GatewayFormController,
43   - controllerAs: 'vm',
44   - templateUrl: gatewayFormTemplate
45   - };
46   -}
47   -
48   -/*@ngInject*/
49   -function GatewayFormController($scope, $injector, $document, $mdExpansionPanel, toast, importExport, attributeService, deviceService, userService, $mdDialog, $mdUtil, types, $window, $q) {
50   - $scope.$mdExpansionPanel = $mdExpansionPanel;
51   - let vm = this;
52   - const attributeNameClinet = "current_configuration";
53   - const attributeNameServer = "configuration_drafts";
54   - const attributeNameShared = "configuration";
55   - const attributeNameLogShared = "RemoteLoggingLevel";
56   - vm.remoteLoggingConfig = '[loggers]}}keys=root, service, connector, converter, tb_connection, storage, extension}}[handlers]}}keys=consoleHandler, serviceHandler, connectorHandler, converterHandler, tb_connectionHandler, storageHandler, extensionHandler}}[formatters]}}keys=LogFormatter}}[logger_root]}}level=ERROR}}handlers=consoleHandler}}[logger_connector]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=connector}}[logger_storage]}}level={ERROR}}}handlers=storageHandler}}formatter=LogFormatter}}qualname=storage}}[logger_tb_connection]}}level={ERROR}}}handlers=tb_connectionHandler}}formatter=LogFormatter}}qualname=tb_connection}}[logger_service]}}level={ERROR}}}handlers=serviceHandler}}formatter=LogFormatter}}qualname=service}}[logger_converter]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=converter}}[logger_extension]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=extension}}[handler_consoleHandler]}}class=StreamHandler}}level={ERROR}}}formatter=LogFormatter}}args=(sys.stdout,)}}[handler_connectorHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}connector.log", "d", 1, 7,)}}[handler_storageHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}storage.log", "d", 1, 7,)}}[handler_serviceHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}service.log", "d", 1, 7,)}}[handler_converterHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}converter.log", "d", 1, 3,)}}[handler_extensionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}extension.log", "d", 1, 3,)}}[handler_tb_connectionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}tb_connection.log", "d", 1, 3,)}}[formatter_LogFormatter]}}format="%(asctime)s - %(levelname)s - [%(filename)s] - %(module)s - %(lineno)d - %(message)s" }}datefmt="%Y-%m-%d %H:%M:%S"';
57   - vm.types = types;
58   -
59   - vm.configurations = {
60   - singleSelect: '',
61   - host: $document[0].domain,
62   - port: 1883,
63   - remoteConfiguration: true,
64   - accessToken: '',
65   - entityType: '',
66   - entityId: '',
67   - storageType: "memoryStorage", // "memoryStorage"; fileStorage
68   - readRecordsCount: 100,
69   - maxRecordsCount: 10000,
70   - dataFolderPath: './data/',
71   - maxFilesCount: 5,
72   - securityType: "accessToken", // "accessToken", "tls"
73   - caCertPath: '/etc/thingsboard-gateway/ca.pem',
74   - privateKeyPath: '/etc/thingsboard-gateway/privateKey.pem',
75   - certPath: '/etc/thingsboard-gateway/certificate.pem',
76   - connectors: {},
77   - remoteLoggingLevel: "DEBUG", // level login
78   - remoteLoggingPathToLogs: './logs/'
79   - };
80   - getGatewaysListByUser(true);
81   -
82   - vm.securityTypes = [{
83   - name: 'Access Token',
84   - value: 'accessToken'
85   - }, {
86   - name: 'TLS',
87   - value: 'tls'
88   - }];
89   -
90   - vm.storageTypes = [{
91   - name: 'Memory storage',
92   - value: 'memoryStorage'
93   - }, {
94   - name: 'File storage',
95   - value: 'fileStorage'
96   - }];
97   -
98   - $scope.$on('gateway-form-resize', function (event, formId) {
99   - if (vm.formId == formId) {
100   - updateWidgetDisplaying();
101   - }
102   - });
103   -
104   - function updateWidgetDisplaying() {
105   - if (vm.ctx && vm.ctx.$container) {
106   - vm.changeAlignment = (vm.ctx.$container[0].offsetWidth <= 425);
107   - }
108   - }
109   -
110   - updateWidgetDisplaying();
111   -
112   - vm.getAccessToken = (deviceObj) => {
113   - if (deviceObj.name) {
114   - deviceService.findByName(deviceObj.name, {ignoreErrors: true})
115   - .then(
116   - function (device) {
117   - getDeviceCredential(device.id.id);
118   - }
119   - )
120   - }
121   - };
122   -
123   - function getDeviceCredential(deviceId) {
124   - return deviceService.getDeviceCredentials(deviceId).then(
125   - (deviceCredentials) => {
126   - vm.configurations.accessToken = deviceCredentials.credentialsId;
127   - vm.configurations.entityType = deviceCredentials.deviceId.entityType;
128   - vm.configurations.entityId = deviceCredentials.deviceId.id;
129   - vm.getAttributeStart();
130   - }
131   - );
132   - }
133   -
134   - vm.createDevice = (deviceObj) => {
135   - deviceService.findByName(deviceObj.name, {ignoreErrors: true})
136   - .then(
137   - function (device) {
138   - getDeviceCredential(device.id.id).then(() => {
139   - getGatewaysListByUser();
140   - });
141   - },
142   - function () {
143   - deviceService.saveDevice(deviceObj).then(
144   - (device) => {
145   - deviceService.getDeviceCredentials(device.id.id).then(
146   - (data) => {
147   - vm.configurations.accessToken = data.credentialsId;
148   - vm.configurations.entityType = device.id.entityType;
149   - vm.configurations.entityId = device.id.id;
150   - vm.getAttributeStart();
151   - getGatewaysListByUser();
152   - }
153   - );
154   - }
155   - );
156   - });
157   - };
158   -
159   - vm.saveAttributeConfig = () => {
160   - vm.setAttribute(attributeNameShared, $window.btoa(angular.toJson(vm.getConfigAllByAttributeJSON())), types.attributesScope.shared.value);
161   - vm.setAttribute(attributeNameServer, $window.btoa(angular.toJson(vm.getConfigByAttributeTmpJSON())), types.attributesScope.server.value);
162   - vm.setAttribute(attributeNameLogShared, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value);
163   - };
164   -
165   - vm.getAttributeStart = () => {
166   - let initResps = [];
167   - vm.configurations.connectors = {};
168   - initResps.push(vm.getAttributeConfig(attributeNameClinet, types.attributesScope.client.value));
169   - initResps.push(vm.getAttributeConfig(attributeNameServer, types.attributesScope.server.value));
170   - initResps.push(vm.getAttributeConfig(attributeNameLogShared, types.attributesScope.shared.value));
171   - $q.all(initResps).then((resp) => {
172   - vm.getAttributeInitFromClient(resp[0]);
173   - vm.getAttributeInitFromServer(resp[1]);
174   - vm.getAttributeInitFromShared(resp[2]);
175   - }, (err) => {
176   - console.log("getAttribute_error", err); //eslint-disable-line
177   - });
178   - };
179   -
180   - vm.getAttributeConfig = (attributeName, typeValue) => {
181   - let keys = [attributeName];
182   - return attributeService.getEntityAttributesValues(vm.configurations.entityType, vm.configurations.entityId, typeValue, keys);
183   - };
184   -
185   - vm.setAttribute = (attributeName, attributeConfig, typeValue) => {
186   - let attributes = [
187   - {
188   - key: attributeName,
189   - value: attributeConfig
190   - }
191   - ];
192   - attributeService.saveEntityAttributes(vm.configurations.entityType, vm.configurations.entityId, typeValue, attributes).then(() => {
193   - }, (err) => {
194   - console.log("setAttribute_", err); //eslint-disable-line
195   - });
196   - };
197   -
198   - vm.exportConfig = () => {
199   - let fileZip = {};
200   - fileZip["tb_gateway.yaml"] = vm.getConfig();
201   - vm.createConfigByExport(fileZip);
202   - vm.getLogsConfigByExport(fileZip);
203   - importExport.exportJSZip(fileZip, 'config');
204   - vm.setAttribute(attributeNameLogShared, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value);
205   - };
206   -
207   - vm.getConfig = () => {
208   - let config;
209   - config = 'thingsboard:\n';
210   - config += ' host: ' + vm.configurations.host + '\n';
211   - config += ' remoteConfiguration: ' + vm.configurations.remoteConfiguration + '\n';
212   - config += ' port: ' + vm.configurations.port + '\n';
213   - config += ' security:\n';
214   - if (vm.configurations.securityType === 'accessToken') {
215   - config += ' access-token: ' + vm.configurations.accessToken + '\n';
216   - } else if (vm.configurations.securityType === 'tls') {
217   - config += ' ca_cert: ' + vm.configurations.caCertPath + '\n';
218   - config += ' privateKey: ' + vm.configurations.privateKeyPath + '\n';
219   - config += ' cert: ' + vm.configurations.certPath + '\n';
220   - }
221   - config += 'storage:\n';
222   - if (vm.configurations.storageType === 'memoryStorage') {
223   - config += ' type: memory\n';
224   - config += ' read_records_count: ' + vm.configurations.readRecordsCount + '\n';
225   - config += ' max_records_count: ' + vm.configurations.maxRecordsCount + '\n';
226   - } else if (vm.configurations.storageType === 'fileStorage') {
227   - config += ' type: file\n';
228   - config += ' data_folder_path: ' + vm.configurations.dataFolderPath + '\n';
229   - config += ' max_file_count: ' + vm.configurations.maxFilesCount + '\n';
230   - config += ' max_read_records_count: ' + vm.configurations.readRecordsCount + '\n';
231   - config += ' max_records_per_file: ' + vm.configurations.maxRecordsCount + '\n';
232   - }
233   - config += 'connectors:\n';
234   - for (let connector in vm.configurations.connectors) {
235   - if (vm.configurations.connectors[connector].enabled) {
236   - config += ' -\n';
237   - config += ' name: ' + connector + ' Connector\n';
238   - config += ' type: ' + vm.configurations.connectors[connector].connector + '\n';
239   - config += ' configuration: ' + vm.validFileName(connector) + ".json" + '\n';
240   - }
241   - }
242   - return config;
243   - };
244   -
245   - vm.createConfigByExport = (fileZipAdd) => {
246   - for (let connector in vm.configurations.connectors) {
247   - if (vm.configurations.connectors[connector].enabled) {
248   - fileZipAdd[vm.validFileName(connector) + ".json"] = angular.toJson(vm.configurations.connectors[connector].config);
249   - }
250   - }
251   - };
252   -
253   - vm.getLogsConfigByExport = (fileZipAdd) => {
254   - fileZipAdd["logs.conf"] = vm.getLogsConfig();
255   - };
256   -
257   - vm.getLogsConfig = () => {
258   - return vm.remoteLoggingConfig
259   - .replace(/{ERROR}/g, vm.configurations.remoteLoggingLevel)
260   - .replace(/{.\/logs\/}/g, vm.configurations.remoteLoggingPathToLogs);
261   - };
262   -
263   - vm.getConfigAllByAttributeJSON = () => {
264   - let thingsBoardAll = {};
265   - thingsBoardAll["thingsboard"] = vm.getConfigMainByAttributeJSON();
266   - vm.getConfigByAttributeJSON(thingsBoardAll);
267   - return thingsBoardAll;
268   - };
269   -
270   - vm.getConfigMainByAttributeJSON = () => {
271   - let configMain = {};
272   - let thingsBoard = {};
273   - thingsBoard.host = vm.configurations.host;
274   - thingsBoard.remoteConfiguration = vm.configurations.remoteConfiguration;
275   - thingsBoard.port = vm.configurations.port;
276   - let security = {};
277   - if (vm.configurations.securityType === 'accessToken') {
278   - security.accessToken = (vm.configurations.accessToken) ? vm.configurations.accessToken : ""
279   - } else {
280   - security.caCert = vm.configurations.caCertPath;
281   - security.privateKey = vm.configurations.privateKeyPath;
282   - security.cert = vm.configurations.certPath;
283   - }
284   - thingsBoard.security = security;
285   - configMain.thingsboard = thingsBoard;
286   -
287   - let storage = {};
288   - if (vm.configurations.storageType === 'memoryStorage') {
289   - storage.type = "memory";
290   - storage.read_records_count = vm.configurations.readRecordsCount;
291   - storage.max_records_count = vm.configurations.maxRecordsCount;
292   - } else if (vm.configurations.storageType === 'fileStorage') {
293   - storage.type = "file";
294   - storage.data_folder_path = vm.configurations.dataFolderPath;
295   - storage.max_file_count = vm.configurations.maxFilesCount;
296   - storage.max_read_records_count = vm.configurations.readRecordsCount;
297   - storage.max_records_per_file = vm.configurations.maxRecordsCount;
298   - }
299   - configMain.storage = storage;
300   -
301   - let conn = [];
302   - for (let connector in vm.configurations.connectors) {
303   - if (vm.configurations.connectors[connector].enabled) {
304   - let connect = {};
305   - connect.configuration = vm.validFileName(connector) + ".json";
306   - connect.name = connector;
307   - connect.type = vm.configurations.connectors[connector].connector;
308   - conn.push(connect);
309   - }
310   - }
311   - configMain.connectors = conn;
312   -
313   - configMain.logs = $window.btoa(vm.getLogsConfig());
314   -
315   - return configMain;
316   - };
317   -
318   - vm.getConfigByAttributeJSON = (thingsBoardBy) => {
319   - for (let connector in vm.configurations.connectors) {
320   - if (vm.configurations.connectors[connector].enabled) {
321   - let typeAr = vm.configurations.connectors[connector].connector;
322   - let objTypeAll = [];
323   - for (let conn in vm.configurations.connectors) {
324   - if (typeAr === vm.configurations.connectors[conn].connector && vm.configurations.connectors[conn].enabled) {
325   - let objType = {};
326   - objType["name"] = conn;
327   - objType["config"] = vm.configurations.connectors[conn].config;
328   - objTypeAll.push(objType);
329   - }
330   - }
331   - if (objTypeAll.length > 0) {
332   - thingsBoardBy[typeAr] = objTypeAll;
333   - }
334   - }
335   - }
336   - };
337   -
338   - vm.getConfigByAttributeTmpJSON = () => {
339   - let connects = {};
340   - for (let connector in vm.configurations.connectors) {
341   - if (!vm.configurations.connectors[connector].enabled && Object.keys(vm.configurations.connectors[connector].config).length !== 0) {
342   - let conn = {};
343   - conn["connector"] = vm.configurations.connectors[connector].connector;
344   - conn["config"] = vm.configurations.connectors[connector].config;
345   - connects[connector] = conn;
346   - }
347   - }
348   - return connects;
349   - };
350   -
351   - function getGatewaysListByUser(firstInit) {
352   - vm.gateways = [];
353   - vm.currentUser = userService.getCurrentUser();
354   - if (vm.currentUser.authority === 'TENANT_ADMIN') {
355   - deviceService.getTenantDevices({limit: 500}).then(
356   - (devices) => {
357   - if (devices.data.length > 0) {
358   - devices.data.forEach((device) => {
359   - if (device.additionalInfo !== null && device.additionalInfo.gateway === true) {
360   - vm.gateways.push(device.name);
361   - if (firstInit && vm.gateways.length && device.name === vm.gateways[0]) {
362   - vm.configurations.singleSelect = vm.gateways[0];
363   - let deviceObj = {
364   - "name": vm.configurations.singleSelect,
365   - "type": "Gateway",
366   - "additionalInfo": {
367   - "gateway": true
368   - }
369   - };
370   - vm.getAccessToken(deviceObj);
371   - }
372   - }
373   - });
374   - }
375   - }
376   - );
377   - } else if (vm.currentUser.authority === 'CUSTOMER_USER') {
378   - deviceService.getCustomerDevices(vm.currentUser.customerId, {limit: 500}).then(
379   - (devices) => {
380   - if (devices.data.length > 0) {
381   - devices.data.forEach((device) => {
382   - if (device.additionalInfo !== null && device.additionalInfo.gateway === true) {
383   - vm.gateways.push(device.name);
384   - if (firstInit && vm.gateways.length) {
385   - vm.configurations.singleSelect = vm.gateways[0];
386   - let deviceObj = {
387   - "name": vm.configurations.singleSelect,
388   - "type": "Gateway",
389   - "additionalInfo": {
390   - "gateway": true
391   - }
392   - };
393   - vm.getAccessToken(deviceObj);
394   - }
395   - }
396   - });
397   - }
398   - }
399   - );
400   - }
401   - }
402   -
403   - vm.getAttributeInitFromClient = (resp) => {
404   - if (resp.length > 0) {
405   - vm.configurations.connectors = {};
406   - let attribute = angular.fromJson($window.atob(resp[0].value));
407   - for (var type in attribute) {
408   - let keyVal = attribute[type];
409   - if (type === "thingsboard") {
410   - if (keyVal !== null && Object.keys(keyVal).length > 0) {
411   - vm.setConfigMain(keyVal);
412   - }
413   - } else {
414   - for (let typeVal in keyVal) {
415   - let typeName = '';
416   - if (Object.prototype.hasOwnProperty.call(keyVal[typeVal], 'name')) {
417   - typeName = 'name';
418   - }
419   - let key = "";
420   - key = (typeName === "") ? "No name" : ((typeName === 'name') ? keyVal[typeVal].name : keyVal[typeVal][typeName].name);
421   - let conn = {};
422   - conn["enabled"] = true;
423   - conn["connector"] = type;
424   - conn["config"] = angular.toJson(keyVal[typeVal].config);
425   - vm.configurations.connectors[key] = conn;
426   - }
427   - }
428   - }
429   - }
430   - };
431   -
432   - vm.getAttributeInitFromServer = (resp) => {
433   - if (resp.length > 0) {
434   - let attribute = angular.fromJson($window.atob(resp[0].value));
435   - for (let key in attribute) {
436   - let conn = {};
437   - conn["enabled"] = false;
438   - conn["connector"] = attribute[key].connector;
439   - conn["config"] = angular.toJson(attribute[key].config);
440   - vm.configurations.connectors[key] = conn;
441   - }
442   - }
443   - };
444   -
445   - vm.getAttributeInitFromShared = (resp) => {
446   - if (resp.length > 0) {
447   - if (vm.types.gatewayLogLevel[resp[0].value.toLowerCase()]) {
448   - vm.configurations.remoteLoggingLevel = resp[0].value.toUpperCase();
449   - }
450   - } else {
451   - vm.configurations.remoteLoggingLevel = vm.types.gatewayLogLevel.debug;
452   - }
453   - };
454   -
455   - vm.setConfigMain = (keyVal) => {
456   - if (Object.prototype.hasOwnProperty.call(keyVal, 'thingsboard')) {
457   - vm.configurations.host = keyVal.thingsboard.host;
458   - vm.configurations.port = keyVal.thingsboard.port;
459   - vm.configurations.remoteConfiguration = keyVal.thingsboard.remoteConfiguration;
460   - if (Object.prototype.hasOwnProperty.call(keyVal.thingsboard.security, 'accessToken')) {
461   - vm.configurations.securityType = 'accessToken';
462   - vm.configurations.accessToken = keyVal.thingsboard.security.accessToken;
463   - } else {
464   - vm.configurations.securityType = 'tls';
465   - vm.configurations.caCertPath = keyVal.thingsboard.security.caCert;
466   - vm.configurations.privateKeyPath = keyVal.thingsboard.security.private_key;
467   - vm.configurations.certPath = keyVal.thingsboard.security.cert;
468   - }
469   - }
470   - if (Object.prototype.hasOwnProperty.call(keyVal, 'storage') && Object.prototype.hasOwnProperty.call(keyVal.storage, 'type')) {
471   - if (keyVal.storage.type === 'memory') {
472   - vm.configurations.storageType = 'memoryStorage';
473   - vm.configurations.readRecordsCount = keyVal.storage.read_records_count;
474   - vm.configurations.maxRecordsCount = keyVal.storage.max_records_count;
475   - } else if (keyVal.storage.type === 'file') {
476   - vm.configurations.storageType = 'fileStorage';
477   - vm.configurations.dataFolderPath = keyVal.storage.data_folder_path;
478   - vm.configurations.maxFilesCount = keyVal.storage.max_file_count;
479   - vm.configurations.readRecordsCount = keyVal.storage.read_records_count;
480   - vm.configurations.maxRecordsCount = keyVal.storage.max_records_count;
481   - }
482   - }
483   - };
484   -
485   - vm.setSaveTypeConfig = (itemVal) => {
486   - vm.configurations.remoteConfiguration = itemVal.item;
487   - };
488   -
489   - vm.validFileName = (fileName) => {
490   - let fileName1 = fileName.replace("_", "");
491   - let fileName2 = fileName1.replace("-", "");
492   - let fileName3 = fileName2.replace(/^\s+|\s+$/g, '');
493   - let fileName4 = fileName3.toLowerCase();
494   - return fileName4;
495   - };
496   -}
497   -
498   -
ui/src/app/components/gateway/gateway-config-dialog.tpl.html renamed from ui/src/app/components/gateWay/gateway-config-dialog.tpl.html
... ... @@ -55,15 +55,15 @@
55 55 required>
56 56 </div>
57 57 </div>
58   - <div class="tb-error-messages" layout="column" flex ng-messages="theForm.config.$error" role="alert">
  58 + <div class="tb-error-messages" layout="column" ng-messages="theForm.config.$error" role="alert">
59 59 <div ng-message="required" flex class="tb-error-message" translate>gateway.json-required</div>
60   - <div ng-message="vm.config" class="tb-error-message" translate>gateway.json-parse</div>
  60 + <div ng-message="config" class="tb-error-message" translate>gateway.json-parse</div>
61 61 </div>
62 62 </div>
63 63 </div>
64 64 </md-dialog-content>
65 65 <md-dialog-actions layout="row" layout-align="end center" class="action-buttons">
66   - <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
  66 + <md-button ng-disabled="$root.loading || theForm.config.$invalid || !theForm.config.$dirty" type="submit"
67 67 class="md-raised md-primary">
68 68 {{'action.save'|translate}}
69 69 </md-button>
... ...
ui/src/app/components/gateway/gateway-config-select.directive.js renamed from ui/src/app/components/gateWay/gateway-config-select.directive.js
... ... @@ -17,7 +17,7 @@ import './gateway-config-select.scss';
17 17
18 18 /* eslint-disable import/no-unresolved, import/default */
19 19
20   -import gatewayAliasSelectTemplate from './gateway-config-select.tpl.html';
  20 +import gatewaySelectTemplate from './gateway-config-select.tpl.html';
21 21
22 22 /* eslint-enable import/no-unresolved, import/default */
23 23
... ... @@ -32,23 +32,26 @@ export default angular.module('thingsboard.directives.gatewayConfigSelect', [])
32 32 function GatewayConfigSelect($compile, $templateCache, $mdConstant, $translate, $mdDialog) {
33 33
34 34 var linker = function (scope, element, attrs, ngModelCtrl) {
35   - var template = $templateCache.get(gatewayAliasSelectTemplate);
  35 + const template = $templateCache.get(gatewaySelectTemplate);
36 36 element.html(template);
37 37
38 38 scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
39   -
40   - scope.ngModelCtrl = ngModelCtrl;
41   - scope.singleSelect = null;
  39 + scope.gateway = null;
  40 + scope.gatewaySearchText = '';
42 41
43 42 scope.updateValidity = function () {
44 43 var value = ngModelCtrl.$viewValue;
45 44 var valid = angular.isDefined(value) && value != null || !scope.tbRequired;
46   - ngModelCtrl.$setValidity('singleSelect', valid);
  45 + ngModelCtrl.$setValidity('gateway', valid);
47 46 };
48 47
49   - scope.$watch('singleSelect', function () {
50   - scope.updateView();
51   - });
  48 + function startWatchers() {
  49 + scope.$watch('gateway', function (newVal, prevVal) {
  50 + if (!angular.equals(newVal, prevVal) && newVal !== null) {
  51 + scope.updateView();
  52 + }
  53 + });
  54 + }
52 55
53 56 scope.gatewayNameSearch = function (gatewaySearchText) {
54 57 return gatewaySearchText ? scope.gatewayList.filter(
... ... @@ -58,22 +61,20 @@ function GatewayConfigSelect($compile, $templateCache, $mdConstant, $translate,
58 61 scope.createFilterForGatewayName = function (query) {
59 62 var lowercaseQuery = query.toLowerCase();
60 63 return function filterFn(device) {
61   - return (device.toLowerCase().indexOf(lowercaseQuery) === 0);
  64 + return (device.name.toLowerCase().indexOf(lowercaseQuery) === 0);
62 65 };
63 66 };
64 67
65 68 scope.updateView = function () {
66   - ngModelCtrl.$setViewValue(scope.singleSelect);
  69 + ngModelCtrl.$setViewValue(scope.gateway);
67 70 scope.updateValidity();
68   - let deviceObj = {"name": scope.singleSelect, "type": "Gateway", "additionalInfo": {
69   - "gateway": true
70   - }};
71   - scope.getAccessToken(deviceObj);
  71 + scope.getAccessToken(scope.gateway.id);
72 72 };
73 73
74 74 ngModelCtrl.$render = function () {
75 75 if (ngModelCtrl.$viewValue) {
76   - scope.singleSelect = ngModelCtrl.$viewValue;
  76 + scope.gateway = ngModelCtrl.$viewValue;
  77 + startWatchers();
77 78 }
78 79 };
79 80
... ... @@ -85,9 +86,9 @@ function GatewayConfigSelect($compile, $templateCache, $mdConstant, $translate,
85 86 if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) {
86 87 $event.preventDefault();
87 88 let indexRes = scope.gatewayList.findIndex((element) => element.key === scope.gatewaySearchText);
88   - if (indexRes === -1) {
89   - scope.createNewGatewayDialog($event, {name: scope.gatewaySearchText});
90   - }
  89 + if (indexRes === -1) {
  90 + scope.createNewGatewayDialog($event, scope.gatewaySearchText);
  91 + }
91 92 }
92 93 };
93 94
... ... @@ -96,7 +97,7 @@ function GatewayConfigSelect($compile, $templateCache, $mdConstant, $translate,
96 97 $event.stopPropagation();
97 98 }
98 99 var title = $translate.instant('gateway.create-new-gateway');
99   - var content = $translate.instant('gateway.create-new-gateway-text', {gatewayName: deviceName.name});
  100 + var content = $translate.instant('gateway.create-new-gateway-text', {gatewayName: deviceName});
100 101 var confirm = $mdDialog.confirm()
101 102 .targetEvent($event)
102 103 .title(title)
... ... @@ -106,9 +107,13 @@ function GatewayConfigSelect($compile, $templateCache, $mdConstant, $translate,
106 107 .ok($translate.instant('action.yes'));
107 108 $mdDialog.show(confirm).then(
108 109 () => {
109   - let deviceObj = {"name": deviceName.name, "type": "Gateway", "additionalInfo": {
110   - "gateway": true
111   - }};
  110 + let deviceObj = {
  111 + name: deviceName,
  112 + type: "Gateway",
  113 + additionalInfo: {
  114 + gateway: true
  115 + }
  116 + };
112 117 scope.createDevice(deviceObj);
113 118 },
114 119 () => {
... ... @@ -125,7 +130,6 @@ function GatewayConfigSelect($compile, $templateCache, $mdConstant, $translate,
125 130 link: linker,
126 131 scope: {
127 132 tbRequired: '=?',
128   - allowedEntityTypes: '=?',
129 133 gatewayList: '=?',
130 134 getAccessToken: '=',
131 135 createDevice: '=',
... ...
ui/src/app/components/gateway/gateway-config-select.scss renamed from ui/src/app/components/gateWay/gateway-config-select.scss
ui/src/app/components/gateway/gateway-config-select.tpl.html renamed from ui/src/app/components/gateWay/gateway-config-select.tpl.html
... ... @@ -16,14 +16,14 @@
16 16
17 17 -->
18 18 <section layout='column'>
19   - <md-autocomplete md-input-name="singleSelect" flex
  19 + <md-autocomplete md-input-name="gateway" flex
20 20 ng-required="tbRequired"
21 21 md-no-cache="true"
22   - ng-model="singleSelect"
23   - md-selected-item="singleSelect"
  22 + ng-model="gateway"
  23 + md-selected-item="gateway"
24 24 md-search-text="gatewaySearchText"
25 25 md-items="item in gatewayNameSearch(gatewaySearchText)"
26   - md-item-text="item"
  26 + md-item-text="item.name"
27 27 tb-keydown="gatewayNameEnter($event)"
28 28 tb-keypress="gatewayNameEnter($event)"
29 29 md-min-length="0"
... ... @@ -33,7 +33,7 @@
33 33 md-input-class="tb-test"
34 34 md-menu-container-class="tb-gateway-autocomplete-container">
35 35 <md-item-template>
36   - <span md-highlight-text="gatewaySearchText" md-highlight-flags="^i">{{item}}</span>
  36 + <span md-highlight-text="gatewaySearchText" md-highlight-flags="^i">{{item.name}}</span>
37 37 </md-item-template>
38 38 <md-not-found>
39 39 <div class="tb-not-found">
... ... @@ -41,14 +41,13 @@
41 41 <span translate>gateway.no-gateway-found</span>
42 42 </div>
43 43 <div ng-if="!textIsEmpty(gatewaySearchText)">
44   - <span translate
45   - translate-values='{ item: "{{gatewaySearchText | truncate:true:6:&apos;...&apos;}}" }'>gateway.no-gateway-matching</span>
46   - <a translate ng-click="createNewGatewayDialog($event, {name: gatewaySearchText})">gateway.create-new-gateway</a>
  44 + <span translate translate-values='{ item: "{{gatewaySearchText | truncate:true:6:&apos;...&apos;}}" }'>gateway.no-gateway-matching</span>
  45 + <a translate ng-click="createNewGatewayDialog($event, gatewaySearchText)">gateway.create-new-gateway</a>
47 46 </div>
48 47 </div>
49 48 </md-not-found>
50   - <div ng-messages="theForm.singleSelect.$error">
51   - <div ng-message="required" translate>Test</div>
  49 + <div ng-messages="theForm.gateway.$error">
  50 + <div ng-message="required" translate>gateway.gateway-name-required</div>
52 51 </div>
53 52 </md-autocomplete>
54 53 </section>
... ...
  1 +/*
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import './gateway-config.scss';
  17 +
  18 +/* eslint-disable import/no-unresolved, import/default */
  19 +
  20 +import gatewayConfigTemplate from './gateway-config.tpl.html';
  21 +import gatewayConfigDialogTemplate from './gateway-config-dialog.tpl.html';
  22 +import beautify from "js-beautify";
  23 +
  24 +/* eslint-enable import/no-unresolved, import/default */
  25 +const js_beautify = beautify.js;
  26 +
  27 +export default angular.module('thingsboard.directives.gatewayConfig', [])
  28 + .directive('tbGatewayConfig', GatewayConfig)
  29 + .name;
  30 +
  31 +/*@ngInject*/
  32 +function GatewayConfig() {
  33 + return {
  34 + restrict: "E",
  35 + scope: true,
  36 + bindToController: {
  37 + disabled: '=ngDisabled',
  38 + gatewayConfig: '=',
  39 + changeAlignment: '=',
  40 + theForm: '='
  41 + },
  42 + controller: GatewayConfigController,
  43 + controllerAs: 'vm',
  44 + templateUrl: gatewayConfigTemplate
  45 + };
  46 +}
  47 +
  48 +/*@ngInject*/
  49 +function GatewayConfigController($scope, $document, $mdDialog, $mdUtil, $window, types) {
  50 + let vm = this;
  51 + vm.types = types;
  52 +
  53 + vm.removeConnector = (index) => {
  54 + if (index > -1) {
  55 + vm.gatewayConfig.splice(index, 1);
  56 + }
  57 + };
  58 +
  59 + vm.addNewConnector = () => {
  60 + vm.gatewayConfig.push({
  61 + enabled: false,
  62 + configType: '',
  63 + config: {},
  64 + name: ''
  65 + });
  66 + };
  67 +
  68 + vm.openConfigDialog = ($event, index, config, typeName) => {
  69 + if ($event) {
  70 + $event.stopPropagation();
  71 + }
  72 + $mdDialog.show({
  73 + controller: GatewayDialogController,
  74 + controllerAs: 'vm',
  75 + templateUrl: gatewayConfigDialogTemplate,
  76 + parent: angular.element($document[0].body),
  77 + locals: {
  78 + config: config,
  79 + typeName: typeName
  80 + },
  81 + targetEvent: $event,
  82 + fullscreen: true,
  83 + multiple: true,
  84 + }).then(function (config) {
  85 + if (config && index > -1) {
  86 + vm.gatewayConfig[index].config = config;
  87 + }
  88 + });
  89 +
  90 + };
  91 +
  92 + vm.changeConnectorType = (connector) => {
  93 + for (let gatewayConfigTypeKey in types.gatewayConfigType) {
  94 + if (types.gatewayConfigType[gatewayConfigTypeKey].value === connector.configType) {
  95 + if (!connector.name) {
  96 + connector.name = generateConnectorName(types.gatewayConfigType[gatewayConfigTypeKey].name, 0);
  97 + break;
  98 + }
  99 + }
  100 + }
  101 + };
  102 +
  103 + vm.changeConnectorName = (connector, currentConnectorIndex) => {
  104 + connector.name = validateConnectorName(connector.name, 0, currentConnectorIndex);
  105 + };
  106 +
  107 + function generateConnectorName(name, index) {
  108 + let newKeyName = index ? name + index : name;
  109 + let indexRes = vm.gatewayConfig.findIndex((element) => element.name === newKeyName);
  110 + return indexRes === -1 ? newKeyName : generateConnectorName(name, ++index);
  111 + }
  112 +
  113 + function validateConnectorName(name, index, currentConnectorIndex) {
  114 + for (let i = 0; i < vm.gatewayConfig.length; i++) {
  115 + let nameEq = (index === 0) ? name : name + index;
  116 + if (i !== currentConnectorIndex && vm.gatewayConfig[i].name === nameEq) {
  117 + index++;
  118 + validateConnectorName(name, index, currentConnectorIndex);
  119 + }
  120 + }
  121 + return (index === 0) ? name : name + index;
  122 + }
  123 +
  124 + vm.validateJSON = (config) => {
  125 + return angular.equals({}, config);
  126 + };
  127 +}
  128 +
  129 +/*@ngInject*/
  130 +function GatewayDialogController($scope, $mdDialog, $document, $window, config, typeName) {
  131 + let vm = this;
  132 + vm.config = js_beautify(angular.toJson(config), {indent_size: 4});
  133 + vm.typeName = typeName;
  134 + vm.configAreaOptions = {
  135 + useWrapMode: true,
  136 + mode: 'json',
  137 + advanced: {
  138 + enableSnippets: true,
  139 + enableBasicAutocompletion: true,
  140 + enableLiveAutocompletion: true
  141 + },
  142 + onLoad: function (_ace) {
  143 + _ace.$blockScrolling = 1;
  144 + }
  145 + };
  146 +
  147 + vm.validateConfig = (model, editorName) => {
  148 + if (model && model.length) {
  149 + try {
  150 + angular.fromJson(model);
  151 + $scope.theForm[editorName].$setValidity('config', true);
  152 + } catch (e) {
  153 + $scope.theForm[editorName].$setValidity('config', false);
  154 + }
  155 + }
  156 + };
  157 +
  158 + vm.save = () => {
  159 + $mdDialog.hide(angular.fromJson(vm.config));
  160 + };
  161 +
  162 + vm.cancel = () => {
  163 + $mdDialog.hide();
  164 + };
  165 +
  166 + vm.beautifyJson = () => {
  167 + vm.config = js_beautify(vm.config, {indent_size: 4});
  168 + };
  169 +}
  170 +
... ...
ui/src/app/components/gateway/gateway-config.scss renamed from ui/src/app/components/gateWay/gateway-config.scss
... ... @@ -56,20 +56,20 @@
56 56 .tb-json-toolbar{
57 57 height: 40px;
58 58 }
  59 +}
59 60
60   - .tb-json-panel {
61   - height: calc(100% - 80px);
62   - margin-left: 15px;
63   - border: 1px solid #c0c0c0;
  61 +.tb-json-panel {
  62 + height: calc(100% - 80px);
  63 + margin-left: 15px;
  64 + border: 1px solid #c0c0c0;
64 65
65   - .tb-json-input {
66   - width: 100%;
67   - min-width: 400px;
68   - height: 100%;
  66 + .tb-json-input {
  67 + width: 100%;
  68 + min-width: 400px;
  69 + height: 100%;
69 70
70   - &:not(.fill-height) {
71   - min-height: 200px;
72   - }
  71 + &:not(.fill-height) {
  72 + min-height: 200px;
73 73 }
74 74 }
75 75 }
... ...
ui/src/app/components/gateway/gateway-config.tpl.html renamed from ui/src/app/components/gateWay/gateway-config.tpl.html
... ... @@ -15,61 +15,49 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<form name="gatewayConfig" flex layout="column" class="gateway-config">
19   - <div layout="row" id="section-row" ng-repeat="keyVal in vm.kvList track by $index">
20   - <div layout="column" layout-align="center center" class="gateway-config-row">
21   - <md-input-container class="md-block">
22   - <md-checkbox ng-model="keyVal.enabled"
23   - aria-label="{{ 'gateway.enabled' | translate }}"
24   - ng-change="vm.checkboxValid(keyVal)"
25   - ng-click="vm.checkboxValidClick($event, keyVal)"
26   - ng-mouseover="vm.checkboxValidMouseover($event, keyVal)">
27   - </md-checkbox>
28   - <md-tooltip md-direction="top">
29   - {{ 'gateway.enabled' | translate }}
30   - </md-tooltip>
31   - </md-input-container>
  18 +<section name="gatewayConfig" layout="column" class="gateway-config">
  19 + <section layout="row" ng-form="gatewayConfig_{{$index}}" ng-repeat="connector in vm.gatewayConfig track by $index">
  20 + <div layout="column" layout-align="center start" class="gateway-config-row">
  21 + <md-switch ng-model="connector.enabled" ng-disabled="gatewayConfig_{{$index}}.$invalid || vm.validateJSON(connector.config)"
  22 + aria-label="{{ 'gateway.connector-enabled' | translate }}">
  23 + </md-switch>
32 24 </div>
33 25 <div layout="row" flex class="gateway-config-row"
34 26 ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
35 27 <md-input-container class="md-block" flex>
36 28 <label>{{'gateway.connector-type' | translate }}</label>
37   - <md-select name="configType" ng-change="vm.configTypeChange(keyVal)" ng-model="keyVal.value" required>
  29 + <md-select name="connectorType"
  30 + ng-change="vm.changeConnectorType(connector)"
  31 + aria-label="{{ 'gateway.gateway.connector-type' | translate }}"
  32 + ng-model="connector.configType" required>
38 33 <md-option ng-repeat="configType in vm.types.gatewayConfigType" ng-value="configType.value">
39 34 {{configType.value}}
40 35 </md-option>
41 36 </md-select>
42   - <md-tooltip md-direction="top">
43   - {{ 'gateway.connector-type' | translate }}
44   - </md-tooltip>
  37 + <div ng-messages="vm.theForm.connectorType.$error">
  38 + <div ng-message="required" translate>gateway.connector-type-required</div>
  39 + </div>
45 40 </md-input-container>
46 41 <md-input-container class="md-block" flex>
47   - <input placeholder="{{ (vm.keyPlaceholderText ? vm.keyPlaceholderText : 'gateway.name') | translate }}"
  42 + <input placeholder="{{'gateway.connector-name' | translate }}"
48 43 ng-model-options="{ updateOn: 'blur' }"
49   - ng-change="vm.keyValChange(keyVal, $index)" name="key" ng-model="keyVal.key" required/>
50   - <div ng-messages="gatewayConfig.key.$error">
51   - <div ng-message="required" translate>extension.field-required</div>
  44 + ng-change="vm.changeConnectorName(connector, $index)" name="connectorName" ng-model="connector.name" required/>
  45 + <div ng-messages="vm.theForm.connectorName.$error">
  46 + <div ng-message="required" translate>gateway.connector-name-required</div>
52 47 </div>
53   - <md-tooltip md-direction="top">
54   - {{ 'gateway.name' | translate }}
55   - </md-tooltip>
56 48 </md-input-container>
57 49 </div>
58 50 <div layout="row" layout-align="end center" class="action-buttons"
59 51 ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
60   - <md-button class="md-icon-button md-fab md-mini"
61   - name="updateconf"
62   - ng-click="vm.openConfigDialog($event, $index, keyVal.config, keyVal.key)"
  52 + <md-button class="md-icon-button md-mini" ng-click="vm.openConfigDialog($event, $index, connector.config, connector.name)"
63 53 aria-label="{{ 'gateway.update-config' | translate }}"
64   - ng-class="vm.buttonValid(keyVal.config)" required>
65   - <md-icon class="material-icons">settings_ethernet</md-icon>
  54 + ng-class="{'md-warn': vm.validateJSON(connector.config)}">
  55 + <md-icon class="material-icons">more_horiz</md-icon>
66 56 <md-tooltip md-direction="top">
67 57 {{ 'gateway.update-config' | translate }}
68 58 </md-tooltip>
69 59 </md-button>
70   - <md-button ng-show="!vm.disabled" ng-disabled="$root.loading"
71   - class="md-icon-button md-fab md-mini md-primary"
72   - ng-click="vm.removeKeyVal($index)"
  60 + <md-button class="md-icon-button md-mini" ng-click="vm.removeConnector($index)"
73 61 aria-label="{{ 'gateway.delete' | translate }}">
74 62 <md-icon class="material-icons">close</md-icon>
75 63 <md-tooltip md-direction="top">
... ... @@ -77,18 +65,17 @@
77 65 </md-tooltip>
78 66 </md-button>
79 67 </div>
80   - </div>
81   - <span ng-show="!vm.kvList.length"
  68 + </section>
  69 + <span ng-show="!vm.gatewayConfig.length"
82 70 layout-align="center center" ng-class="{'disabled': vm.disabled}"
83   - class="no-data-found" translate>{{vm.noDataText ? vm.noDataText : 'gateway.no-connectors'}}</span>
  71 + class="no-data-found" translate>{{'gateway.no-connectors'}}</span>
84 72 <div>
85   - <md-button ng-show="!vm.disabled" ng-disabled="$root.loading" class="md-raised"
86   - ng-click="vm.addKeyVal()"
87   - aria-label="{{ 'gateway.add-connectors' | translate }}">
  73 + <md-button class="md-raised" ng-click="vm.addNewConnector()"
  74 + aria-label="{{ 'gateway.connector-add' | translate }}">
88 75 <md-tooltip md-direction="top">
89   - {{ 'gateway.add-connectors' | translate }}
  76 + {{ 'gateway.connector-add' | translate }}
90 77 </md-tooltip>
91 78 <span translate>action.add</span>
92 79 </md-button>
93 80 </div>
94   -</form >
  81 +</section >
... ...
  1 +/*
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import './gateway-form.scss';
  17 +/* eslint-disable import/no-unresolved, import/default */
  18 +
  19 +import gatewayFormTemplate from './gateway-form.tpl.html';
  20 +
  21 +/* eslint-enable import/no-unresolved, import/default */
  22 +
  23 +export default angular.module('thingsboard.directives.gatewayForm', [])
  24 + .directive('tbGatewayForm', GatewayForm)
  25 + .name;
  26 +
  27 +/*@ngInject*/
  28 +function GatewayForm() {
  29 + return {
  30 + restrict: "E",
  31 + scope: true,
  32 + bindToController: {
  33 + formId: '=',
  34 + ctx: '='
  35 + },
  36 + controller: GatewayFormController,
  37 + controllerAs: 'vm',
  38 + templateUrl: gatewayFormTemplate
  39 + };
  40 +}
  41 +
  42 +/*@ngInject*/
  43 +function GatewayFormController($scope, $injector, $document, $mdExpansionPanel, toast, importExport, attributeService, deviceService, userService, $mdDialog, $mdUtil, types, $window, $q, entityService, utils, $translate) {
  44 + let vm = this;
  45 + const currentConfigurationAttribute = "current_configuration";
  46 + const configurationDraftsAttribute = "configuration_drafts";
  47 + const configurationAttribute = "configuration";
  48 + const remoteLoggingLevelAttribute = "RemoteLoggingLevel";
  49 +
  50 + const templateLogsConfig = '[loggers]}}keys=root, service, connector, converter, tb_connection, storage, extension}}[handlers]}}keys=consoleHandler, serviceHandler, connectorHandler, converterHandler, tb_connectionHandler, storageHandler, extensionHandler}}[formatters]}}keys=LogFormatter}}[logger_root]}}level=ERROR}}handlers=consoleHandler}}[logger_connector]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=connector}}[logger_storage]}}level={ERROR}}}handlers=storageHandler}}formatter=LogFormatter}}qualname=storage}}[logger_tb_connection]}}level={ERROR}}}handlers=tb_connectionHandler}}formatter=LogFormatter}}qualname=tb_connection}}[logger_service]}}level={ERROR}}}handlers=serviceHandler}}formatter=LogFormatter}}qualname=service}}[logger_converter]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=converter}}[logger_extension]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=extension}}[handler_consoleHandler]}}class=StreamHandler}}level={ERROR}}}formatter=LogFormatter}}args=(sys.stdout,)}}[handler_connectorHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}connector.log", "d", 1, 7,)}}[handler_storageHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}storage.log", "d", 1, 7,)}}[handler_serviceHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}service.log", "d", 1, 7,)}}[handler_converterHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}converter.log", "d", 1, 3,)}}[handler_extensionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}extension.log", "d", 1, 3,)}}[handler_tb_connectionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}tb_connection.log", "d", 1, 3,)}}[formatter_LogFormatter]}}format="%(asctime)s - %(levelname)s - [%(filename)s] - %(module)s - %(lineno)d - %(message)s" }}datefmt="%Y-%m-%d %H:%M:%S"';
  51 +
  52 + vm.types = types;
  53 +
  54 + vm.configurations = {
  55 + gateway: '',
  56 + host: $document[0].domain,
  57 + port: 1883,
  58 + remoteConfiguration: true,
  59 + accessToken: '',
  60 + storageType: "memoryStorage",
  61 + readRecordsCount: 100,
  62 + maxRecordsCount: 10000,
  63 + dataFolderPath: './data/',
  64 + maxFilesCount: 5,
  65 + securityType: "accessToken",
  66 + caCertPath: '/etc/thingsboard-gateway/ca.pem',
  67 + privateKeyPath: '/etc/thingsboard-gateway/privateKey.pem',
  68 + certPath: '/etc/thingsboard-gateway/certificate.pem',
  69 + connectors: [],
  70 + remoteLoggingLevel: "DEBUG",
  71 + remoteLoggingPathToLogs: './logs/'
  72 + };
  73 +
  74 + let archiveFileName = '';
  75 + let gatewayNameExists = '';
  76 + let successfulSaved = '';
  77 +
  78 + vm.securityTypes = [{
  79 + name: 'gateway.security-types.access-token',
  80 + value: 'accessToken'
  81 + }, {
  82 + name: 'gateway.security-types.tls',
  83 + value: 'tls'
  84 + }];
  85 +
  86 + vm.storageTypes = [{
  87 + name: 'gateway.storage-types.memory-storage',
  88 + value: 'memoryStorage'
  89 + }, {
  90 + name: 'gateway.storage-types.file-storage',
  91 + value: 'fileStorage'
  92 + }];
  93 +
  94 + $scope.$watch('vm.ctx', function () {
  95 + if (vm.ctx ) {
  96 + vm.settings = vm.ctx.settings;
  97 + vm.widgetConfig = vm.ctx.widgetConfig;
  98 + initializeConfig();
  99 + }
  100 + });
  101 +
  102 + $scope.$on('gateway-form-resize', function (event, formId) {
  103 + if (vm.formId == formId) {
  104 + updateWidgetDisplaying();
  105 + }
  106 + });
  107 +
  108 + function updateWidgetDisplaying() {
  109 + vm.changeAlignment = (vm.ctx.$container[0].offsetWidth <= 425);
  110 + }
  111 +
  112 + function initWidgetSettings() {
  113 + let widgetTitle;
  114 + if (vm.settings.widgetTitle && vm.settings.widgetTitle.length) {
  115 + widgetTitle = utils.customTranslation(vm.settings.widgetTitle, vm.settings.widgetTitle);
  116 + } else {
  117 + widgetTitle = $translate.instant('gateway.gateway');
  118 + }
  119 + vm.ctx.widgetTitle = widgetTitle;
  120 +
  121 + archiveFileName = vm.settings.archiveFileName && vm.settings.archiveFileName.length ? vm.settings.archiveFileName : 'gatewayConfiguration';
  122 + gatewayNameExists = utils.customTranslation(vm.settings.deviceNameExist, vm.settings.deviceNameExist) || $translate.instant('gateway.gateway-exists');
  123 + successfulSaved = utils.customTranslation(vm.settings.successfulSave, vm.settings.successfulSave) || $translate.instant('gateway.gateway-saved');
  124 + }
  125 +
  126 + function initializeConfig() {
  127 + updateWidgetDisplaying();
  128 + initWidgetSettings();
  129 + getGatewaysList(true);
  130 + }
  131 +
  132 + vm.getAccessToken = (deviceId) => {
  133 + if (deviceId.id) {
  134 + getDeviceCredentials(deviceId.id);
  135 + }
  136 + };
  137 +
  138 + vm.collapsePanel = function (panelId) {
  139 + $mdExpansionPanel(panelId).collapse();
  140 + };
  141 +
  142 + function getDeviceCredentials(deviceId) {
  143 + return deviceService.getDeviceCredentials(deviceId).then(
  144 + (deviceCredentials) => {
  145 + vm.configurations.accessToken = deviceCredentials.credentialsId;
  146 + getAttributes();
  147 + }
  148 + );
  149 + }
  150 +
  151 + vm.createDevice = (deviceObj) => {
  152 + deviceService.findByName(deviceObj.name, {ignoreErrors: true})
  153 + .then(
  154 + function () {
  155 + toast.showError(gatewayNameExists, angular.element('.gateway-form'),'top left');
  156 + },
  157 + function () {
  158 + if(vm.settings.gatewayType && vm.settings.gatewayType.length){
  159 + deviceObj.type = vm.settings.gatewayType;
  160 + }
  161 + deviceService.saveDevice(deviceObj).then(
  162 + (device) => {
  163 + getDeviceCredentials(device.id.id).then(() =>{
  164 + getGatewaysList();
  165 + });
  166 + }
  167 + );
  168 + });
  169 + };
  170 +
  171 + vm.saveAttributeConfig = () => {
  172 + $q.all([
  173 + saveAttribute(configurationAttribute, $window.btoa(angular.toJson(getGatewayConfigJSON())), types.attributesScope.shared.value),
  174 + saveAttribute(configurationDraftsAttribute, $window.btoa(angular.toJson(getDraftConnectorJSON())), types.attributesScope.server.value),
  175 + saveAttribute(remoteLoggingLevelAttribute, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value)
  176 + ]).then(() =>{
  177 + toast.showSuccess(successfulSaved, 2000, angular.element('.gateway-form'),'top left');
  178 + })
  179 + };
  180 +
  181 + function getAttributes() {
  182 + let promises = [];
  183 + promises.push(getAttribute(currentConfigurationAttribute, types.attributesScope.client.value));
  184 + promises.push(getAttribute(configurationDraftsAttribute, types.attributesScope.server.value));
  185 + promises.push(getAttribute(remoteLoggingLevelAttribute, types.attributesScope.shared.value));
  186 + $q.all(promises).then((response) => {
  187 + processCurrentConfiguration(response[0]);
  188 + processConfigurationDrafts(response[1]);
  189 + processLoggingLevel(response[2]);
  190 + });
  191 + }
  192 +
  193 + function getAttribute(attributeName, attributeScope) {
  194 + return attributeService.getEntityAttributesValues(vm.configurations.gateway.id.entityType, vm.configurations.gateway.id.id, attributeScope, attributeName);
  195 + }
  196 +
  197 + function saveAttribute(attributeName, attributeValue, attributeScope) {
  198 + let attributes = [{
  199 + key: attributeName,
  200 + value: attributeValue
  201 + }];
  202 + return attributeService.saveEntityAttributes(vm.configurations.gateway.id.entityType, vm.configurations.gateway.id.id, attributeScope, attributes);
  203 + }
  204 +
  205 + vm.exportConfig = () => {
  206 + let filesZip = {};
  207 + filesZip["tb_gateway.yaml"] = generateYAMLConfigurationFile();
  208 + generateConfigConnectorFiles(filesZip);
  209 + generateLogConfigFile(filesZip);
  210 + importExport.exportJSZip(filesZip, archiveFileName);
  211 + saveAttribute(remoteLoggingLevelAttribute, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value);
  212 + };
  213 +
  214 + function generateYAMLConfigurationFile() {
  215 + let config;
  216 + config = 'thingsboard:\n';
  217 + config += ' host: ' + vm.configurations.host + '\n';
  218 + config += ' remoteConfiguration: ' + vm.configurations.remoteConfiguration + '\n';
  219 + config += ' port: ' + vm.configurations.port + '\n';
  220 + config += ' security:\n';
  221 + if (vm.configurations.securityType === 'accessToken') {
  222 + config += ' access-token: ' + vm.configurations.accessToken + '\n';
  223 + } else if (vm.configurations.securityType === 'tls') {
  224 + config += ' ca_cert: ' + vm.configurations.caCertPath + '\n';
  225 + config += ' privateKey: ' + vm.configurations.privateKeyPath + '\n';
  226 + config += ' cert: ' + vm.configurations.certPath + '\n';
  227 + }
  228 + config += 'storage:\n';
  229 + if (vm.configurations.storageType === 'memoryStorage') {
  230 + config += ' type: memory\n';
  231 + config += ' read_records_count: ' + vm.configurations.readRecordsCount + '\n';
  232 + config += ' max_records_count: ' + vm.configurations.maxRecordsCount + '\n';
  233 + } else if (vm.configurations.storageType === 'fileStorage') {
  234 + config += ' type: file\n';
  235 + config += ' data_folder_path: ' + vm.configurations.dataFolderPath + '\n';
  236 + config += ' max_file_count: ' + vm.configurations.maxFilesCount + '\n';
  237 + config += ' max_read_records_count: ' + vm.configurations.readRecordsCount + '\n';
  238 + config += ' max_records_per_file: ' + vm.configurations.maxRecordsCount + '\n';
  239 + }
  240 + config += 'connectors:\n';
  241 + for(let i = 0; i < vm.configurations.connectors.length; i++){
  242 + if (vm.configurations.connectors[i].enabled) {
  243 + config += ' -\n';
  244 + config += ' name: ' + vm.configurations.connectors[i].name + '\n';
  245 + config += ' type: ' + vm.configurations.connectors[i].configType + '\n';
  246 + config += ' configuration: ' + generateFileName(vm.configurations.connectors[i].name) + '\n';
  247 + }
  248 + }
  249 + return config;
  250 + }
  251 +
  252 + function generateConfigConnectorFiles(fileZipAdd) {
  253 + for(let i = 0; i < vm.configurations.connectors.length; i++){
  254 + if (vm.configurations.connectors[i].enabled) {
  255 + fileZipAdd[generateFileName(vm.configurations.connectors[i].name)] = angular.toJson(vm.configurations.connectors[i].config);
  256 + }
  257 + }
  258 + }
  259 +
  260 + function generateLogConfigFile(fileZipAdd) {
  261 + fileZipAdd["logs.conf"] = getLogsConfig();
  262 + }
  263 +
  264 + function getLogsConfig() {
  265 + return templateLogsConfig
  266 + .replace(/{ERROR}/g, vm.configurations.remoteLoggingLevel)
  267 + .replace(/{.\/logs\/}/g, vm.configurations.remoteLoggingPathToLogs);
  268 + }
  269 +
  270 + function getGatewayConfigJSON() {
  271 + let gatewayConfig = {};
  272 + gatewayConfig["thingsboard"] = gatewayMainConfigJSON();
  273 + gatewayConnectorConfigJSON(gatewayConfig);
  274 + return gatewayConfig;
  275 + }
  276 +
  277 + function gatewayMainConfigJSON() {
  278 + let configuration = {};
  279 +
  280 + let thingsBoard = {};
  281 + thingsBoard.host = vm.configurations.host;
  282 + thingsBoard.remoteConfiguration = vm.configurations.remoteConfiguration;
  283 + thingsBoard.port = vm.configurations.port;
  284 + let security = {};
  285 + if (vm.configurations.securityType === 'accessToken') {
  286 + security.accessToken = (vm.configurations.accessToken) ? vm.configurations.accessToken : ""
  287 + } else {
  288 + security.caCert = vm.configurations.caCertPath;
  289 + security.privateKey = vm.configurations.privateKeyPath;
  290 + security.cert = vm.configurations.certPath;
  291 + }
  292 + thingsBoard.security = security;
  293 + configuration.thingsboard = thingsBoard;
  294 +
  295 + let storage = {};
  296 + if (vm.configurations.storageType === 'memoryStorage') {
  297 + storage.type = "memory";
  298 + storage.read_records_count = vm.configurations.readRecordsCount;
  299 + storage.max_records_count = vm.configurations.maxRecordsCount;
  300 + } else if (vm.configurations.storageType === 'fileStorage') {
  301 + storage.type = "file";
  302 + storage.data_folder_path = vm.configurations.dataFolderPath;
  303 + storage.max_file_count = vm.configurations.maxFilesCount;
  304 + storage.max_read_records_count = vm.configurations.readRecordsCount;
  305 + storage.max_records_per_file = vm.configurations.maxRecordsCount;
  306 + }
  307 + configuration.storage = storage;
  308 +
  309 + let connectors = [];
  310 + for (let i = 0; i < vm.configurations.connectors.length; i++) {
  311 + if (vm.configurations.connectors[i].enabled) {
  312 + let connector = {
  313 + configuration: generateFileName(vm.configurations.connectors[i].name),
  314 + name: vm.configurations.connectors[i].name,
  315 + type: vm.configurations.connectors[i].configType
  316 + };
  317 + connectors.push(connector);
  318 + }
  319 + }
  320 + configuration.connectors = connectors;
  321 +
  322 + configuration.logs = $window.btoa(getLogsConfig());
  323 +
  324 + return configuration;
  325 + }
  326 +
  327 + function gatewayConnectorConfigJSON(gatewayConfiguration) {
  328 + for(let i = 0; i < vm.configurations.connectors.length; i++){
  329 + if (vm.configurations.connectors[i].enabled) {
  330 + let typeConnector = vm.configurations.connectors[i].configType;
  331 + if(!angular.isArray(gatewayConfiguration[typeConnector])){
  332 + gatewayConfiguration[typeConnector] = [];
  333 + }
  334 +
  335 + let connectorConfig = {
  336 + name: vm.configurations.connectors[i].name,
  337 + config: vm.configurations.connectors[i].config
  338 + };
  339 + gatewayConfiguration[typeConnector].push(connectorConfig);
  340 + }
  341 + }
  342 + }
  343 +
  344 + function getDraftConnectorJSON() {
  345 + let draftConnector = {};
  346 + for(let i = 0; i < vm.configurations.connectors.length; i++){
  347 + if (!vm.configurations.connectors[i].enabled) {
  348 + let connector = {
  349 + connector: vm.configurations.connectors[i].configType,
  350 + config: vm.configurations.connectors[i].config
  351 + };
  352 + draftConnector[vm.configurations.connectors[i].name] = connector;
  353 + }
  354 + }
  355 + return draftConnector;
  356 + }
  357 +
  358 + function getGatewaysList(firstInit) {
  359 + vm.gateways = [];
  360 + entityService.getEntitiesByNameFilter(types.entityType.device, "", -1).then((devices) => {
  361 + for (let i = 0; i < devices.length; i++) {
  362 + const device = devices[i];
  363 + if (device.additionalInfo !== null && device.additionalInfo.gateway === true) {
  364 + vm.gateways.push(device);
  365 + if (firstInit && vm.gateways.length && device.name === vm.gateways[0].name) {
  366 + vm.configurations.gateway = device;
  367 + vm.getAccessToken(device.id);
  368 + }
  369 + }
  370 + }
  371 + });
  372 + }
  373 +
  374 + function processCurrentConfiguration(response) {
  375 + if (response.length > 0) {
  376 + vm.configurations.connectors = [];
  377 + let attribute = angular.fromJson($window.atob(response[0].value));
  378 + for (var attributeKey in attribute) {
  379 + let keyValue = attribute[attributeKey];
  380 + if (attributeKey === "thingsboard") {
  381 + if (keyValue !== null && Object.keys(keyValue).length > 0) {
  382 + setConfigGateway(keyValue);
  383 + }
  384 + } else {
  385 + for (let connectorType in keyValue) {
  386 + let name = "No name";
  387 + if (Object.prototype.hasOwnProperty.call(keyValue[connectorType], 'name')) {
  388 + name = keyValue[connectorType].name ;
  389 + }
  390 + let connector = {
  391 + enabled: true,
  392 + configType: attributeKey,
  393 + config: keyValue[connectorType].config,
  394 + name: name
  395 + };
  396 + vm.configurations.connectors.push(connector);
  397 + }
  398 + }
  399 + }
  400 + }
  401 + }
  402 +
  403 + function processConfigurationDrafts(response) {
  404 + if (response.length > 0) {
  405 + let attribute = angular.fromJson($window.atob(response[0].value));
  406 + for (let key in attribute) {
  407 + let connector = {
  408 + enabled: false,
  409 + configType: attribute[key].connector,
  410 + config: attribute[key].config,
  411 + name: key
  412 + };
  413 + vm.configurations.connectors.push(connector);
  414 + }
  415 + }
  416 + }
  417 +
  418 + function processLoggingLevel(response) {
  419 + if (response.length > 0) {
  420 + if (vm.types.gatewayLogLevel[response[0].value.toLowerCase()]) {
  421 + vm.configurations.remoteLoggingLevel = response[0].value.toUpperCase();
  422 + }
  423 + } else {
  424 + vm.configurations.remoteLoggingLevel = vm.types.gatewayLogLevel.debug;
  425 + }
  426 + }
  427 +
  428 + function setConfigGateway(keyValue) {
  429 + if (Object.prototype.hasOwnProperty.call(keyValue, 'thingsboard')) {
  430 + vm.configurations.host = keyValue.thingsboard.host;
  431 + vm.configurations.port = keyValue.thingsboard.port;
  432 + vm.configurations.remoteConfiguration = keyValue.thingsboard.remoteConfiguration;
  433 + if (Object.prototype.hasOwnProperty.call(keyValue.thingsboard.security, 'accessToken')) {
  434 + vm.configurations.securityType = 'accessToken';
  435 + vm.configurations.accessToken = keyValue.thingsboard.security.accessToken;
  436 + } else {
  437 + vm.configurations.securityType = 'tls';
  438 + vm.configurations.caCertPath = keyValue.thingsboard.security.caCert;
  439 + vm.configurations.privateKeyPath = keyValue.thingsboard.security.private_key;
  440 + vm.configurations.certPath = keyValue.thingsboard.security.cert;
  441 + }
  442 + }
  443 +
  444 + if (Object.prototype.hasOwnProperty.call(keyValue, 'storage') && Object.prototype.hasOwnProperty.call(keyValue.storage, 'type')) {
  445 + if (keyValue.storage.type === 'memory') {
  446 + vm.configurations.storageType = 'memoryStorage';
  447 + vm.configurations.readRecordsCount = keyValue.storage.read_records_count;
  448 + vm.configurations.maxRecordsCount = keyValue.storage.max_records_count;
  449 + } else if (keyValue.storage.type === 'file') {
  450 + vm.configurations.storageType = 'fileStorage';
  451 + vm.configurations.dataFolderPath = keyValue.storage.data_folder_path;
  452 + vm.configurations.maxFilesCount = keyValue.storage.max_file_count;
  453 + vm.configurations.readRecordsCount = keyValue.storage.read_records_count;
  454 + vm.configurations.maxRecordsCount = keyValue.storage.max_records_count;
  455 + }
  456 + }
  457 + }
  458 +
  459 + function generateFileName(fileName) {
  460 + return fileName.replace("_", "")
  461 + .replace("-", "")
  462 + .replace(/^\s+|\s+/g, '')
  463 + .toLowerCase() + '.json';
  464 + }
  465 +}
  466 +
  467 +
... ...
ui/src/app/components/gateway/gateway-form.scss renamed from ui/src/app/components/gateWay/gateway-form.scss
... ... @@ -14,7 +14,9 @@
14 14 * limitations under the License.
15 15 */
16 16 .gateway-form{
  17 + height: 100%;
17 18 padding: 5px 5px 0;
  19 + background-color: transparent;
18 20
19 21 .gateway-form-row{
20 22 md-input-container{
... ... @@ -30,11 +32,11 @@
30 32 }
31 33 }
32 34
  35 + .security-type {
  36 + margin-top: 18px;
  37 + }
  38 +
33 39 .form-action-buttons{
34 40 padding-top: 8px;
35 41 }
36 42 }
37   -
38   -.security-type {
39   - margin-top: 38px;
40   -}
... ...
ui/src/app/components/gateway/gateway-form.tpl.html renamed from ui/src/app/components/gateWay/gateway-form.tpl.html
... ... @@ -15,205 +15,213 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<form name="gatewayConfiguration" class="gateway-form">
19   - <md-expansion-panel-group>
20   - <md-expansion-panel md-component-id="thingsboardPanelId">
21   - <md-expansion-panel-collapsed>
22   - <div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div>
23   - <span flex></span>
24   - <md-expansion-panel-icon></md-expansion-panel-icon>
25   - </md-expansion-panel-collapsed>
26   - <md-expansion-panel-expanded>
27   - <md-expansion-panel-header ng-click="$mdExpansionPanel('thingsboardPanelId').collapse()">
  18 +<md-content md-scroll-y layout="column" class="gateway-form">
  19 + <form name="gatewayConfiguration">
  20 + <md-expansion-panel-group>
  21 + <md-expansion-panel md-component-id="thingsboardPanelId">
  22 + <md-expansion-panel-collapsed>
28 23 <div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div>
29 24 <span flex></span>
30 25 <md-expansion-panel-icon></md-expansion-panel-icon>
31   - </md-expansion-panel-header>
32   - <md-expansion-panel-content>
33   - <tb-gateway-config-select tb-required="true"
34   - ng-model="vm.configurations.singleSelect"
35   - the-form="gatewayConfiguration"
36   - gateway-list="vm.gateways"
37   - get_access_token="vm.getAccessToken"
38   - create-device="vm.createDevice">
39   - </tb-gateway-config-select>
40   - <md-input-container class="md-block">
41   - <label>{{'gateway.security-type' | translate }}</label>
42   - <md-select name="securityType" ng-model="vm.configurations.securityType">
43   - <md-option ng-repeat="securityType in vm.securityTypes" ng-value="securityType.value">
44   - {{securityType.name}}
45   - </md-option>
46   - </md-select>
47   - </md-input-container>
48   - <div layout="row" class="gateway-form-row"
49   - ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
50   - <md-input-container class="md-block" flex>
51   - <label>{{ 'gateway.thingsboard-host' | translate }}</label>
52   - <input type="text" name="host" ng-model="vm.configurations.host" required>
53   - <div ng-messages="gatewayConfiguration.host.$error">
54   - <div ng-message="required" translate>extension.field-required</div>
55   - </div>
56   - </md-input-container>
57   - <md-input-container class="md-block" flex>
58   - <label>{{ 'gateway.thingsboard-port' | translate }}</label>
59   - <input type="number" min="1" max="65535" step="1" name="port"
60   - ng-model="vm.configurations.port" required>
61   - <div ng-messages="gatewayConfiguration.port.$error">
62   - <div ng-message="required" translate>extension.field-required</div>
63   - <div ng-message="max" translate>max</div>
64   - <div ng-message="min" translate>min</div>
65   - </div>
66   - </md-input-container>
67   - </div>
68   - <div ng-if="vm.configurations.securityType=='tls'">
69   - <md-input-container class="md-block security-type">
70   - <label>{{'gateway.tls-path-ca-certificate' | translate }}</label>
71   - <input type="text" ng-model="vm.configurations.caCertPath" name="caCertPath"/>
72   - </md-input-container>
  26 + </md-expansion-panel-collapsed>
  27 + <md-expansion-panel-expanded>
  28 + <md-expansion-panel-header ng-click="vm.collapsePanel('thingsboardPanelId')">
  29 + <div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div>
  30 + <span flex></span>
  31 + <md-expansion-panel-icon></md-expansion-panel-icon>
  32 + </md-expansion-panel-header>
  33 + <md-expansion-panel-content>
  34 + <tb-gateway-config-select tb-required="true"
  35 + ng-model="vm.configurations.gateway"
  36 + the-form="gatewayConfiguration"
  37 + gateway-list="vm.gateways"
  38 + get_access_token="vm.getAccessToken"
  39 + create-device="vm.createDevice">
  40 + </tb-gateway-config-select>
73 41 <md-input-container class="md-block">
74   - <label>{{'gateway.tls-path-private-key' | translate }}</label>
75   - <input type="text" ng-model="vm.configurations.privateKeyPath" name="privateKeyPath"/>
76   - </md-input-container>
77   - <md-input-container class="md-block">
78   - <label>{{'gateway.tls-path-client-certificate' | translate }}</label>
79   - <input type="text" ng-model="vm.configurations.certPath" name="certPath"/>
80   - </md-input-container>
81   - </div>
82   - <md-checkbox ng-model="vm.configurations.remoteConfiguration"
83   - name="remoteConfiguration"
84   - ng-click="vm.setSaveTypeConfig({item: vm.configurations.remoteConfiguration})"
85   - aria-label="{{ 'gateway.remote-tip' | translate }}">
86   - {{ 'gateway.remote' | translate }}
87   - <md-tooltip md-direction="right">{{'gateway.remote-tip' | translate }}</md-tooltip>
88   - </md-checkbox>
89   - <div layout="row" class="gateway-form-row"
90   - ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
91   - <md-input-container class="md-block md-select-container" flex>
92   - <label>{{'gateway.remote-logging-level' | translate }}</label>
93   - <md-select name="loggingLevel" ng-model="vm.configurations.remoteLoggingLevel">
94   - <md-option ng-repeat="loggingLevel in vm.types.gatewayLogLevel"
95   - ng-value="loggingLevel" ng-selected="$index === 5">
96   - {{loggingLevel}}
  42 + <label>{{'gateway.security-type' | translate }}</label>
  43 + <md-select name="securityType" ng-model="vm.configurations.securityType" required>
  44 + <md-option ng-repeat="securityType in vm.securityTypes" ng-value="securityType.value">
  45 + {{securityType.name | translate}}
97 46 </md-option>
98 47 </md-select>
99 48 </md-input-container>
100   - <md-input-container class="md-block" flex>
101   - <label>{{'gateway.remote-logging-path-logs' | translate }}</label>
102   - <input type="text" ng-model="vm.configurations.remoteLoggingPathToLogs"
103   - name="remoteLoggingPathToLogs" required>
104   - <div ng-messages="gatewayConfiguration.remoteLoggingPathToLogs.$error">
105   - <div ng-message="required" translate>extension.field-required</div>
106   - </div>
107   - </md-input-container>
108   - </div>
109   - </md-expansion-panel-content>
110   - </md-expansion-panel-expanded>
111   - </md-expansion-panel>
112   - <md-expansion-panel md-component-id="storagePanelId">
113   - <md-expansion-panel-collapsed>
114   - <div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div>
115   - <span flex></span>
116   - <md-expansion-panel-icon></md-expansion-panel-icon>
117   - </md-expansion-panel-collapsed>
118   - <md-expansion-panel-expanded>
119   - <md-expansion-panel-header ng-click="$mdExpansionPanel('storagePanelId').collapse()">
  49 + <div layout="row" class="gateway-form-row"
  50 + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
  51 + <md-input-container class="md-block" flex>
  52 + <label>{{ 'gateway.thingsboard-host' | translate }}</label>
  53 + <input type="text" name="host" ng-model="vm.configurations.host" required>
  54 + <div ng-messages="gatewayConfiguration.host.$error">
  55 + <div ng-message="required" translate>gateway.thingsboard-host-required</div>
  56 + </div>
  57 + </md-input-container>
  58 + <md-input-container class="md-block" flex>
  59 + <label>{{ 'gateway.thingsboard-port' | translate }}</label>
  60 + <input type="number" min="1" max="65535" ng-pattern="/^-?[0-9]+$/" name="port"
  61 + ng-model="vm.configurations.port" required>
  62 + <div ng-messages="gatewayConfiguration.port.$error">
  63 + <div ng-message="required" translate>gateway.thingsboard-port-required</div>
  64 + <div ng-message="max" translate>gateway.thingsboard-port-max</div>
  65 + <div ng-message="min" translate>gateway.thingsboard-port-min</div>
  66 + <div ng-message="pattern" translate>gateway.thingsboard-port-pattern</div>
  67 + </div>
  68 + </md-input-container>
  69 + </div>
  70 + <div ng-if="vm.configurations.securityType=='tls'">
  71 + <md-input-container class="md-block security-type">
  72 + <label>{{'gateway.tls-path-ca-certificate' | translate }}</label>
  73 + <input type="text" ng-model="vm.configurations.caCertPath" name="caCertPath" />
  74 + </md-input-container>
  75 + <md-input-container class="md-block">
  76 + <label>{{'gateway.tls-path-private-key' | translate }}</label>
  77 + <input type="text" ng-model="vm.configurations.privateKeyPath" name="privateKeyPath" />
  78 + </md-input-container>
  79 + <md-input-container class="md-block">
  80 + <label>{{'gateway.tls-path-client-certificate' | translate }}</label>
  81 + <input type="text" ng-model="vm.configurations.certPath" name="certPath" />
  82 + </md-input-container>
  83 + </div>
  84 + <md-checkbox ng-model="vm.configurations.remoteConfiguration"
  85 + name="remoteConfiguration"
  86 + aria-label="{{ 'gateway.remote-remote' | translate }}">
  87 + {{ 'gateway.remote' | translate }}
  88 + </md-checkbox>
  89 + <div layout="row" class="gateway-form-row"
  90 + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
  91 + <md-input-container class="md-block md-select-container" flex>
  92 + <label>{{'gateway.remote-logging-level' | translate }}</label>
  93 + <md-select name="loggingLevel" ng-model="vm.configurations.remoteLoggingLevel">
  94 + <md-option ng-repeat="logLevel in vm.types.gatewayLogLevel"
  95 + ng-value="logLevel" ng-selected="$index === 5">
  96 + {{logLevel}}
  97 + </md-option>
  98 + </md-select>
  99 + </md-input-container>
  100 + <md-input-container class="md-block" flex>
  101 + <label>{{'gateway.path-logs' | translate }}</label>
  102 + <input type="text" ng-model="vm.configurations.remoteLoggingPathToLogs"
  103 + name="remoteLoggingPathToLogs" required>
  104 + <div ng-messages="gatewayConfiguration.remoteLoggingPathToLogs.$error">
  105 + <div ng-message="required" translate>gateway.path-logs-required</div>
  106 + </div>
  107 + </md-input-container>
  108 + </div>
  109 + </md-expansion-panel-content>
  110 + </md-expansion-panel-expanded>
  111 + </md-expansion-panel>
  112 + <md-expansion-panel md-component-id="storagePanelId">
  113 + <md-expansion-panel-collapsed>
120 114 <div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div>
121 115 <span flex></span>
122 116 <md-expansion-panel-icon></md-expansion-panel-icon>
123   - </md-expansion-panel-header>
124   - <md-expansion-panel-content>
125   - <md-input-container class="md-block" flex>
126   - <label>{{'gateway.storage-type' | translate }}</label>
127   - <md-select required ng-model="vm.configurations.storageType">
128   - <md-option ng-repeat="storageType in vm.storageTypes" ng-value="storageType.value">
129   - {{storageType.name}}
130   - </md-option>
131   - </md-select>
132   - </md-input-container>
133   -
134   - <div layout="row" class="gateway-form-row"
135   - ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
  117 + </md-expansion-panel-collapsed>
  118 + <md-expansion-panel-expanded>
  119 + <md-expansion-panel-header ng-click="vm.collapsePanel('storagePanelId')">
  120 + <div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div>
  121 + <span flex></span>
  122 + <md-expansion-panel-icon></md-expansion-panel-icon>
  123 + </md-expansion-panel-header>
  124 + <md-expansion-panel-content>
136 125 <md-input-container class="md-block" flex>
137   - <label>{{'gateway.storage-read-time' | translate }}</label>
138   - <input type="number" min="1" name="readRecordsCount"
139   - ng-model='vm.configurations.readRecordsCount' required/>
140   - <div ng-messages="gatewayConfiguration.readRecordsCount.$error">
141   - <div ng-message="required" translate>extension.field-required</div>
142   - </div>
  126 + <label>{{'gateway.storage-type' | translate }}</label>
  127 + <md-select required ng-model="vm.configurations.storageType">
  128 + <md-option ng-repeat="storageType in vm.storageTypes" ng-value="storageType.value">
  129 + {{storageType.name | translate}}
  130 + </md-option>
  131 + </md-select>
143 132 </md-input-container>
144 133
145   - <md-input-container class="md-block" flex>
146   - <label>{{'gateway.storage-max-time' | translate }}</label>
147   - <input type="number" min="1" name="maxRecordsCount"
148   - ng-model='vm.configurations.maxRecordsCount' required/>
149   - <div ng-messages="gatewayConfiguration.maxRecordsCount.$error">
150   - <div ng-message="required" translate>extension.field-required</div>
151   - </div>
152   - </md-input-container>
153   - </div>
  134 + <div layout="row" class="gateway-form-row"
  135 + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
  136 + <md-input-container class="md-block" flex>
  137 + <label>{{'gateway.storage-pack-size' | translate }}</label>
  138 + <input type="number" min="1" name="readRecordsCount" ng-pattern="/^-?[0-9]+$/"
  139 + ng-model='vm.configurations.readRecordsCount' required/>
  140 + <div ng-messages="gatewayConfiguration.readRecordsCount.$error">
  141 + <div ng-message="required" translate>gateway.storage-pack-size-required</div>
  142 + <div ng-message="min" translate>gateway.storage-pack-size-min</div>
  143 + <div ng-message="pattern" translate>gateway.storage-pack-size-pattern</div>
  144 + </div>
  145 + </md-input-container>
154 146
155   - <div layout="row" class="gateway-form-row"
156   - ng-if="vm.configurations.storageType == 'fileStorage'"
157   - ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
158   - <md-input-container class="md-block" flex>
159   - <label>{{'gateway.storage-max-files' | translate }}</label>
160   - <input type="number" min="1" name="maxFilesCount" ng-model='vm.configurations.maxFilesCount'
161   - required/>
162   - <div ng-messages="gatewayConfiguration.maxFilesCount.$error">
163   - <div ng-message="required" translate>extension.field-required</div>
164   - </div>
165   - </md-input-container>
  147 + <md-input-container class="md-block" flex>
  148 + <label translate>{{ vm.configurations.storageType !== 'fileStorage' ? 'gateway.storage-max-records' : 'gateway.storage-max-file-records' }}</label>
  149 + <input type="number" min="1" name="maxRecordsCount" ng-pattern="/^-?[0-9]+$/"
  150 + ng-model='vm.configurations.maxRecordsCount' required/>
  151 + <div ng-messages="gatewayConfiguration.maxRecordsCount.$error">
  152 + <div ng-message="required" translate>gateway.storage-max-records-required</div>
  153 + <div ng-message="min" translate>gateway.storage-max-records-min</div>
  154 + <div ng-message="pattern" translate>gateway.storage-max-records-pattern</div>
  155 + </div>
  156 + </md-input-container>
  157 + </div>
166 158
167   - <md-input-container class="md-block" flex>
168   - <label>{{'gateway.storage-data-path' | translate }}</label>
169   - <input type="text" name="dataFolderPath" ng-model='vm.configurations.dataFolderPath'
170   - required/>
171   - <div ng-messages="gatewayConfiguration.dataFolderPath.$error">
172   - <div ng-message="required" translate>extension.field-required</div>
173   - </div>
174   - </md-input-container>
175   - </div>
176   - </md-expansion-panel-content>
177   - </md-expansion-panel-expanded>
178   - </md-expansion-panel>
179   - <md-expansion-panel md-component-id="connectorsPanelId">
180   - <md-expansion-panel-collapsed>
181   - <div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div>
182   - <span flex></span>
183   - <md-expansion-panel-icon></md-expansion-panel-icon>
184   - </md-expansion-panel-collapsed>
185   - <md-expansion-panel-expanded>
186   - <md-expansion-panel-header ng-click="$mdExpansionPanel('connectorsPanelId').collapse()">
  159 + <div layout="row" class="gateway-form-row"
  160 + ng-if="vm.configurations.storageType == 'fileStorage'"
  161 + ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
  162 + <md-input-container class="md-block" flex>
  163 + <label>{{'gateway.storage-max-files' | translate }}</label>
  164 + <input type="number" min="1" name="maxFilesCount" ng-pattern="/^-?[0-9]+$/"
  165 + ng-model='vm.configurations.maxFilesCount' required/>
  166 + <div ng-messages="gatewayConfiguration.maxFilesCount.$error">
  167 + <div ng-message="required" translate>gateway.storage-max-files-required</div>
  168 + <div ng-message="min" translate>gateway.storage-max-files-min</div>
  169 + <div ng-message="pattern" translate>gateway.storage-max-files-pattern</div>
  170 + </div>
  171 + </md-input-container>
  172 +
  173 + <md-input-container class="md-block" flex>
  174 + <label>{{'gateway.storage-path' | translate }}</label>
  175 + <input type="text" name="dataFolderPath" ng-model='vm.configurations.dataFolderPath'
  176 + required/>
  177 + <div ng-messages="gatewayConfiguration.dataFolderPath.$error">
  178 + <div ng-message="required" translate>gateway.storage-path-required</div>
  179 + </div>
  180 + </md-input-container>
  181 + </div>
  182 + </md-expansion-panel-content>
  183 + </md-expansion-panel-expanded>
  184 + </md-expansion-panel>
  185 + <md-expansion-panel md-component-id="connectorsPanelId">
  186 + <md-expansion-panel-collapsed>
187 187 <div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div>
188 188 <span flex></span>
189 189 <md-expansion-panel-icon></md-expansion-panel-icon>
190   - </md-expansion-panel-header>
191   - <md-expansion-panel-content>
192   - <tb-gateway-config
193   - gateway-config="vm.configurations.connectors"
194   - change-alignment="vm.changeAlignment">
195   - </tb-gateway-config>
196   - </md-expansion-panel-content>
197   - </md-expansion-panel-expanded>
198   - </md-expansion-panel>
199   - </md-expansion-panel-group>
200   - <section layout="row" layout-align="end center" class="form-action-buttons">
201   - <md-button class="md-primary md-raised"
202   - ng-click="vm.exportConfig()"
203   - ng-if="!vm.configurations.remoteConfiguration"
204   - ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty"
205   - aria-label="{{ 'gateway.download-tip' | translate }}">
206   - {{'action.download' | translate }}
207   - <md-tooltip>{{'gateway.download-tip' | translate }}</md-tooltip>
208   - </md-button>
  190 + </md-expansion-panel-collapsed>
  191 + <md-expansion-panel-expanded>
  192 + <md-expansion-panel-header ng-click="vm.collapsePanel('connectorsPanelId')">
  193 + <div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div>
  194 + <span flex></span>
  195 + <md-expansion-panel-icon></md-expansion-panel-icon>
  196 + </md-expansion-panel-header>
  197 + <md-expansion-panel-content>
  198 + <tb-gateway-config
  199 + gateway-config="vm.configurations.connectors"
  200 + the-form="gatewayConfiguration"
  201 + change-alignment="vm.changeAlignment">
  202 + </tb-gateway-config>
  203 + </md-expansion-panel-content>
  204 + </md-expansion-panel-expanded>
  205 + </md-expansion-panel>
  206 + </md-expansion-panel-group>
  207 + <section layout="row" layout-align="end center" class="form-action-buttons">
  208 + <md-button class="md-primary md-raised"
  209 + ng-click="vm.exportConfig()"
  210 + ng-if="!vm.configurations.remoteConfiguration"
  211 + ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty"
  212 + aria-label="{{ 'gateway.download-tip' | translate }}">
  213 + {{'action.download' | translate }}
  214 + <md-tooltip>{{'gateway.download-tip' | translate }}</md-tooltip>
  215 + </md-button>
209 216
210   - <md-button class="md-primary md-raised"
211   - ng-click="vm.saveAttributeConfig()"
212   - ng-if="vm.configurations.remoteConfiguration"
213   - ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty"
214   - aria-label="{{ 'gateway.save-tip' | translate }}">
215   - {{'action.save' | translate }}
216   - <md-tooltip ng-if="vm.configurations.remoteConfiguration">{{'gateway.save-tip' | translate }}</md-tooltip>
217   - </md-button>
218   - </section>
219   -</form>
  217 + <md-button class="md-primary md-raised"
  218 + ng-click="vm.saveAttributeConfig()"
  219 + ng-if="vm.configurations.remoteConfiguration"
  220 + ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty"
  221 + aria-label="{{ 'gateway.save-tip' | translate }}">
  222 + {{'action.save' | translate }}
  223 + <md-tooltip ng-if="vm.configurations.remoteConfiguration">{{'gateway.save-tip' | translate }}</md-tooltip>
  224 + </md-button>
  225 + </section>
  226 + </form>
  227 +</md-content>
... ...
... ... @@ -29,7 +29,7 @@ import * as JSZip from 'jszip';
29 29 export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, $rootScope,
30 30 dashboardUtils, entityService, dashboardService, ruleChainService, widgetService, toast, attributeService) {
31 31
32   - const JSZIP_TYPE = {
  32 + const ZIP_TYPE = {
33 33 mimeType: 'application/zip',
34 34 extension: 'zip'
35 35 };
... ... @@ -989,29 +989,19 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
989 989 dialogElement[0].style.width = dialogElement[0].offsetWidth + 2 + "px";
990 990 }
991 991
992   - /**
993   - *
994   - * @param data
995   - * @param filename
996   - * Warn data !!! Not object, if object, then object convert from object to format txt
997   - * Example: data = {keyNameFile1: valueFile1,
998   - * keyNameFile2: valueFile2...}
999   - * fileName - name file of the arhiv
1000   - */
1001 992 function exportJSZip(data, filename) {
1002 993 let jsZip = new JSZip();
1003 994 for (let keyName in data) {
1004 995 let valueData = data[keyName];
1005 996 jsZip.file(keyName, valueData);
1006 997 }
1007   - jsZip.generateAsync({type: "Blob"}).then(function (content) {
1008   - downloadFile(content, filename, JSZIP_TYPE);
  998 + jsZip.generateAsync({type: "blob"}).then(function (content) {
  999 + downloadFile(content, filename, ZIP_TYPE);
1009 1000 });
1010 1001 }
1011 1002
1012 1003
1013 1004 function downloadFile(data, filename, fileType) {
1014   - console.log("downloadFile", data, filename, fileType); // eslint-disable-line
1015 1005 if (!filename) {
1016 1006 filename = 'download';
1017 1007 }
... ...
... ... @@ -30,9 +30,9 @@ import thingsboardSideMenu from '../components/side-menu.directive';
30 30 import thingsboardNavTree from '../components/nav-tree.directive';
31 31 import thingsboardDashboardAutocomplete from '../components/dashboard-autocomplete.directive';
32 32 import thingsboardKvMap from '../components/kv-map.directive';
33   -import thingsboardGatewayConfig from '../components/gateWay/gateway-config.directive';
34   -import thingsboardGatewayConfigSelect from '../components/gateWay/gateway-config-select.directive';
35   -import thingsboardGatewayForm from '../components/gateWay/gateway-form.directive';
  33 +import thingsboardGatewayConfig from '../components/gateway/gateway-config.directive';
  34 +import thingsboardGatewayConfigSelect from '../components/gateway/gateway-config-select.directive';
  35 +import thingsboardGatewayForm from '../components/gateway/gateway-form.directive';
36 36 import thingsboardJsonObjectEdit from '../components/json-object-edit.directive';
37 37 import thingsboardJsonContent from '../components/json-content.directive';
38 38
... ...
... ... @@ -1126,56 +1126,78 @@
1126 1126 "function": "Function"
1127 1127 },
1128 1128 "gateway": {
1129   - "key": "Key configuration",
1130   - "value": "Value configuration",
1131   - "remove-entry": "Remove configuration",
1132 1129 "add-entry": "Add configuration",
1133   - "no-data": "No configurations",
1134   - "gateway-required": "Gateway is required.",
1135   - "gateway-name": "Gateway name",
  1130 + "connector-add": "Add new connector",
  1131 + "connector-enabled": "Enable connector",
  1132 + "connector-name": "Connector name",
  1133 + "connector-name-required": "Connector name is required.",
  1134 + "connector-type": "Connector type",
  1135 + "connector-type-required": "Connector type is required.",
  1136 + "connectors": "Connectors configuration",
1136 1137 "create-new-gateway": "Create a new gateway",
1137 1138 "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?",
  1139 + "delete": "Delete configuration",
  1140 + "download-tip": "Download configuration file",
  1141 + "gateway": "Gateway",
  1142 + "gateway-exists": "Device with same name is already exists.",
  1143 + "gateway-name": "Gateway name",
  1144 + "gateway-name-required": "Gateway name is required.",
  1145 + "gateway-saved": "Gateway configuration successfully saved.",
  1146 + "json-parse": "Not valid JSON.",
  1147 + "json-required": "Field cannot be empty.",
  1148 + "no-connectors": "No connectors",
  1149 + "no-data": "No configurations",
  1150 + "no-gateway-found": "No gateway found.",
1138 1151 "no-gateway-matching": " '{{item}}' not found.",
1139   - "thingsboard": "ThingsBoard",
1140   - "connectors": "Connectors configuration",
1141   - "thingsboard-host": "ThingsBoard Host",
1142   - "thingsboard-port": "ThingsBoard Port",
  1152 + "path-logs": "Path to log files",
  1153 + "path-logs-required": "Path is required.",
  1154 + "remote": "Remote configuration",
  1155 + "remote-logging-level": "Logging level",
  1156 + "remove-entry": "Remove configuration",
  1157 + "save-tip": "Save configuration file",
1143 1158 "security-type": "Security type",
1144   - "tls-path-ca-certificate": "Path to CA certificate on gateway:",
1145   - "tls-path-private-key": "Path to private key on gateway:",
1146   - "tls-path-client-certificate": "Path to client certificate on gateway:",
  1159 + "security-types": {
  1160 + "access-token": "Access Token",
  1161 + "tls": "TLS"
  1162 + },
1147 1163 "storage": "Storage",
  1164 + "storage-max-file-records": "Maximum records in file",
  1165 + "storage-max-files": "Maximum number of files",
  1166 + "storage-max-files-min": "Minimum number is 1.",
  1167 + "storage-max-files-pattern": "Number is not valid.",
  1168 + "storage-max-files-required": "Number is required.",
  1169 + "storage-max-records": "Maximum records in storage",
  1170 + "storage-max-records-min": "Minimum number of records is 1.",
  1171 + "storage-max-records-pattern": "Number is not valid.",
  1172 + "storage-max-records-required": "Maximum records is required.",
  1173 + "storage-pack-size": "Maximum event pack size",
  1174 + "storage-pack-size-min": "Minimum number is 1.",
  1175 + "storage-pack-size-pattern": "Number is not valid.",
  1176 + "storage-pack-size-required": "Maximum event pack size is required.",
  1177 + "storage-path": "Storage path",
  1178 + "storage-path-required": "Storage path is required.",
1148 1179 "storage-type": "Storage type",
1149   - "storage-read-time": "Read records per time:",
1150   - "storage-max-time": "Maximum records per time:",
1151   - "storage-max-files": "Maximum files:",
1152   - "storage-data-path": "Data folder path:",
1153   - "download-tip": "Download configuration file",
1154   - "save-tip": "Save configuration file",
1155   - "remote-tip": "Allow remote configuration",
1156   - "remote": "Remote configuration",
1157   - "remote-logging-level": "Logging level",
1158   - "remote-logging-path-logs": "Path to logs",
1159   - "connector-type": "Connector type",
1160   - "update-config": "Add/update config JSON",
1161   - "delete": "Delete configuration",
1162   - "title-connectors-json": "Connector {{typeName}} configuration",
1163   - "json-required": "Config json is required for gateway config.",
1164   - "json-parse": "Unable to parse config json for gateway config.",
  1180 + "storage-types": {
  1181 + "file-storage": "File storage",
  1182 + "memory-storage": "Memory storage"
  1183 + },
  1184 + "thingsboard": "ThingsBoard",
  1185 + "thingsboard-host": "ThingsBoard host",
  1186 + "thingsboard-host-required": "Host is required.",
  1187 + "thingsboard-port": "ThingsBoard port",
  1188 + "thingsboard-port-max": "Maximum port number is 65535.",
  1189 + "thingsboard-port-min": "Minimum port number is 1.",
  1190 + "thingsboard-port-pattern": "Port is not valid.",
  1191 + "thingsboard-port-required": "Port is required.",
1165 1192 "tidy": "Tidy",
1166 1193 "tidy-tip": "Tidy config JSON",
1167   - "transformer-json-config": "JSON for the config*",
  1194 + "title-connectors-json": "Connector {{typeName}} configuration",
  1195 + "tls-path-ca-certificate": "Path to CA certificate on gateway",
  1196 + "tls-path-client-certificate": "Path to client certificate on gateway",
  1197 + "tls-path-private-key": "Path to private key on gateway",
1168 1198 "toggle-fullscreen": "Toggle fullscreen",
1169   - "add-connectors": "Add new connectors",
1170   - "no-connectors": "No connectors",
1171   - "enabled": "Enabled",
1172   - "name": "Name",
1173   - "no-gateway-found": "No gateway found.",
1174   - "gateway": "Gateway",
1175   - "keyval-save-err": "Save config error",
1176   - "keyval-name-err": "Please add <b>Name</b>",
1177   - "keyval-type-err": "Please add <b>Connector type</b>",
1178   - "keyval-config-err": "Please add <b>configuration JSON</b>"
  1199 + "transformer-json-config": "Configuration JSON*",
  1200 + "update-config": "Add/update configuration JSON"
1179 1201 },
1180 1202 "grid": {
1181 1203 "delete-item-title": "Are you sure you want to delete this item?",
... ...