Commit d965df8c271f0569b02908ea486922e18465543a

Authored by Andrii Shvaika
2 parents 283ad27c eab6ba45

Merge remote-tracking branch 'origin/master' into develop/2.5

... ... @@ -6,6 +6,38 @@
6 6 },
7 7 "widgetTypes": [
8 8 {
  9 + "alias": "attributes_card",
  10 + "name": "Gateway events",
  11 + "descriptor": {
  12 + "type": "latest",
  13 + "sizeX": 7.5,
  14 + "sizeY": 8,
  15 + "resources": [],
  16 + "templateHtml": "",
  17 + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
  18 + "controllerScript": "let types;\nlet eventsReg = \"eventsReg\";\n\nself.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n types = self.ctx.$scope.$injector.get('types');\n \n if (self.ctx.datasources.length && self.ctx.datasources[0].type === types.datasourceType.entity) {\n getDatasourceKeys(self.ctx.datasources[0]);\n } else {\n processDatasources(self.ctx.datasources);\n }\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, false);\n }\n else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasourceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasourceTitleFontSize = self.ctx.width/12;\n }\n datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nfunction processDatasources(datasources) {\n var i = 0;\n var tbDatasource = datasources[i];\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"<div id='\" + datasourceId +\n \"' class='tbDatasource-container'></div>\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"<div class='tbDatasource-title'>\" +\n tbDatasource.name + \"</div>\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"<table id='\" + tableId +\n \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n \"</td><td id='\" + cellId +\n \"'></td></tr>\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n self.onResize();\n}\n\nfunction getDatasourceKeys (datasource) {\n let attributeService = self.ctx.$scope.$injector.get('attributeService');\n if (datasource.entityId && datasource.entityType) {\n attributeService.getEntityKeys(datasource.entityType, datasource.entityId, \"\" , types.dataKeyType.timeseries).then(\n function(data){\n if (data.length) {\n subscribeForKeys (datasource, data);\n }\n });\n }\n}\n\nfunction subscribeForKeys (datasource, data) {\n let eventsRegVals = self.ctx.settings[eventsReg];\n if (eventsRegVals && eventsRegVals.length > 0) {\n var dataKeys = [];\n data.sort();\n data.forEach(dataValue => {eventsRegVals.forEach(event => {\n if (dataValue.toLowerCase().includes(event.toLowerCase())) {\n var dataKey = {\n type: types.dataKeyType.timeseries,\n name: dataValue,\n label: dataValue,\n settings: {},\n _hash: Math.random()\n };\n dataKeys.push(dataKey);\n }\n })});\n\n if (dataKeys.length) {\n updateSubscription (datasource, dataKeys);\n }\n }\n}\n\nfunction updateSubscription (datasource, dataKeys) {\n var datasources = [\n {\n type: types.datasourceType.entity,\n name: datasource.aliasName,\n aliasName: datasource.aliasName,\n entityAliasId: datasource.entityAliasId,\n dataKeys: dataKeys\n }\n ];\n \n var subscriptionOptions = {\n datasources: datasources,\n useDashboardTimewindow: false,\n type: types.widgetType.latest.value,\n callbacks: {\n onDataUpdated: (subscription) => {\n self.ctx.data = subscription.data;\n self.onDataUpdated();\n }\n }\n };\n \n processDatasources(datasources);\n self.ctx.subscriptionApi.createSubscription(subscriptionOptions, true).then(\n (subscription) => {\n self.ctx.defaultSubscription = subscription;\n }\n );\n}\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\n dataKeysOptional: true\n };\n}\n\n",
  19 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"GatewayEventsForm\",\n \"properties\": {\n \"eventsTitle\": {\n \"title\": \"Gateway events form title\",\n \"type\": \"string\",\n \"default\": \"Gateway Events Form\"\n },\n \"eventsReg\": {\n \"title\": \"Events filten.\",\n \"type\": \"array\",\n \"items\": {\n \"title\": \"Event key contains\",\n \"type\": \"string\"\n }\n }\n }\n },\n \"form\": [\n \"eventsTitle\",\n \"eventsReg\"\n ]\n}",
  20 + "dataKeySettingsSchema": "{}\n",
  21 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Function Math.round\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.826503672916844,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"eventsTitle\":\"Gateway Events Form\",\"eventsReg\":[]},\"title\":\"Gateway events\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
  22 + }
  23 + },
  24 + {
  25 + "alias": "config_form_latest",
  26 + "name": "Gateway configuration (Single device)",
  27 + "descriptor": {
  28 + "type": "latest",
  29 + "sizeX": 7.5,
  30 + "sizeY": 9,
  31 + "resources": [],
  32 + "templateHtml": "<tb-gateway-form\n form-id=\"formId\"\n ctx=\"ctx\"\n is-state-form=\"isStateForm\">\n</tb-gateway-form>",
  33 + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}",
  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 scope.isStateForm = true;\n}\n\nself.onResize = function() {\n self.ctx.$scope.$broadcast('gateway-form-resize', self.ctx.$scope.formId);\n}\n\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\t\t\n dataKeysOptional: true\t\t\n };\n}\n\n",
  35 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"GatewayConfigForm\",\n \"properties\": {\n \"gatewayTitle\": {\n \"title\": \"Gateway form\",\n \"type\": \"string\",\n \"default\": \"Gateway configuration (Single device)\"\n },\n \"readOnly\": {\n \"title\": \"Read Only\",\n \"type\": \"boolean\",\n \"default\": false\n }\n }\n },\n \"form\": [\n \"gatewayTitle\",\n \"readOnly\"\n ]\n}\n",
  36 + "dataKeySettingsSchema": "{}\n",
  37 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"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\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Gateway configuration (Single device)\"}"
  38 + }
  39 + },
  40 + {
9 41 "alias": "extension_configuration_widget",
10 42 "name": "Extensions table",
11 43 "descriptor": {
... ...
... ... @@ -1645,7 +1645,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
1645 1645 addPageLinkToParam(params, pageLink);
1646 1646
1647 1647 Map<String, List<JsonNode>> timeseries = restTemplate.exchange(
1648   - baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/values/timeseries?keys={keys}&interval={interval}&agg={agg}&useStrictDataTypes={useStrictDataTypes}&" + getUrlParams(pageLink),
  1648 + baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/values/timeseries?keys={keys}&interval={interval}&agg={agg}&useStrictDataTypes={useStrictDataTypes}&" + getUrlParamsTs(pageLink),
1649 1649 HttpMethod.GET,
1650 1650 HttpEntity.EMPTY,
1651 1651 new ParameterizedTypeReference<Map<String, List<JsonNode>>>() {
... ... @@ -1997,12 +1997,20 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
1997 1997 }
1998 1998
1999 1999 private String getUrlParams(TimePageLink pageLink) {
  2000 + return getUrlParams(pageLink, "startTime", "endTime");
  2001 + }
  2002 +
  2003 + private String getUrlParamsTs(TimePageLink pageLink) {
  2004 + return getUrlParams(pageLink, "startTs", "endTs");
  2005 + }
  2006 +
  2007 + private String getUrlParams(TimePageLink pageLink, String startTime, String endTime) {
2000 2008 String urlParams = "limit={limit}&ascOrder={ascOrder}";
2001 2009 if (pageLink.getStartTime() != null) {
2002   - urlParams += "&startTime={startTime}";
  2010 + urlParams += "&" + startTime + "={startTime}";
2003 2011 }
2004 2012 if (pageLink.getEndTime() != null) {
2005   - urlParams += "&endTime={endTime}";
  2013 + urlParams += "&" + endTime + "={endTime}";
2006 2014 }
2007 2015 if (pageLink.getIdOffset() != null) {
2008 2016 urlParams += "&offset={offset}";
... ...
... ... @@ -606,13 +606,29 @@ export default angular.module('thingsboard.types', [])
606 606 value: "modbus",
607 607 name: "Modbus"
608 608 },
609   - opc_ua: {
  609 + opcua: {
610 610 value: "opcua",
611 611 name: "OPC-UA"
612 612 },
613 613 ble: {
614 614 value: "ble",
615 615 name: "BLE"
  616 + },
  617 + request: {
  618 + value: "request",
  619 + name: "Request"
  620 + },
  621 + can: {
  622 + value: "can",
  623 + name: "CAN"
  624 + },
  625 + bacnet: {
  626 + value: "bacnet",
  627 + name: "BACnet"
  628 + },
  629 + custom: {
  630 + value: "custom",
  631 + name: "Custom"
616 632 }
617 633 },
618 634 gatewayLogLevel: {
... ...
... ... @@ -37,7 +37,8 @@ function GatewayConfig() {
37 37 disabled: '=ngDisabled',
38 38 gatewayConfig: '=',
39 39 changeAlignment: '=',
40   - theForm: '='
  40 + theForm: '=',
  41 + isReadOnly: '='
41 42 },
42 43 controller: GatewayConfigController,
43 44 controllerAs: 'vm',
... ... @@ -83,6 +84,10 @@ function GatewayConfigController($scope, $document, $mdDialog, $mdUtil, $window,
83 84 multiple: true,
84 85 }).then(function (config) {
85 86 if (config && index > -1) {
  87 + console.log(config); //eslint-disable-line
  88 + if (!angular.equals(vm.gatewayConfig[index].config, config)) {
  89 + $scope.gatewayConfiguration.$setDirty();
  90 + }
86 91 vm.gatewayConfig[index].config = config;
87 92 }
88 93 });
... ...
... ... @@ -18,7 +18,7 @@
18 18 <section name="gatewayConfig" layout="column" class="gateway-config">
19 19 <section layout="row" ng-form="gatewayConfig_{{$index}}" ng-repeat="connector in vm.gatewayConfig track by $index">
20 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)"
  21 + <md-switch ng-disabled="vm.isReadOnly" ng-model="connector.enabled" ng-disabled="gatewayConfig_{{$index}}.$invalid || vm.validateJSON(connector.config)"
22 22 aria-label="{{ 'gateway.connector-enabled' | translate }}">
23 23 </md-switch>
24 24 </div>
... ... @@ -26,7 +26,7 @@
26 26 ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
27 27 <md-input-container class="md-block" flex>
28 28 <label>{{'gateway.connector-type' | translate }}</label>
29   - <md-select name="connectorType"
  29 + <md-select ng-disabled="vm.isReadOnly" name="connectorType"
30 30 ng-change="vm.changeConnectorType(connector)"
31 31 aria-label="{{ 'gateway.gateway.connector-type' | translate }}"
32 32 ng-model="connector.configType" required>
... ... @@ -39,7 +39,7 @@
39 39 </div>
40 40 </md-input-container>
41 41 <md-input-container class="md-block" flex>
42   - <input placeholder="{{'gateway.connector-name' | translate }}"
  42 + <input ng-disabled="vm.isReadOnly" placeholder="{{'gateway.connector-name' | translate }}"
43 43 ng-model-options="{ updateOn: 'blur' }"
44 44 ng-change="vm.changeConnectorName(connector, $index)" name="connectorName" ng-model="connector.name" required/>
45 45 <div ng-messages="vm.theForm.connectorName.$error">
... ... @@ -49,7 +49,7 @@
49 49 </div>
50 50 <div layout="row" layout-align="end center" class="action-buttons"
51 51 ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
52   - <md-button class="md-icon-button md-mini" ng-click="vm.openConfigDialog($event, $index, connector.config, connector.name)"
  52 + <md-button ng-disabled="vm.isReadOnly" class="md-icon-button md-mini" ng-click="vm.openConfigDialog($event, $index, connector.config, connector.name)"
53 53 aria-label="{{ 'gateway.update-config' | translate }}"
54 54 ng-class="{'md-warn': vm.validateJSON(connector.config)}">
55 55 <md-icon class="material-icons">more_horiz</md-icon>
... ... @@ -57,7 +57,7 @@
57 57 {{ 'gateway.update-config' | translate }}
58 58 </md-tooltip>
59 59 </md-button>
60   - <md-button class="md-icon-button md-mini" ng-click="vm.removeConnector($index)"
  60 + <md-button ng-disabled="vm.isReadOnly" class="md-icon-button md-mini" ng-click="vm.removeConnector($index)"
61 61 aria-label="{{ 'gateway.delete' | translate }}">
62 62 <md-icon class="material-icons">close</md-icon>
63 63 <md-tooltip md-direction="top">
... ... @@ -70,7 +70,7 @@
70 70 layout-align="center center" ng-class="{'disabled': vm.disabled}"
71 71 class="no-data-found" translate>{{'gateway.no-connectors'}}</span>
72 72 <div>
73   - <md-button class="md-raised" ng-click="vm.addNewConnector()"
  73 + <md-button ng-show="!vm.isReadOnly" class="md-raised" ng-click="vm.addNewConnector()"
74 74 aria-label="{{ 'gateway.connector-add' | translate }}">
75 75 <md-tooltip md-direction="top">
76 76 {{ 'gateway.connector-add' | translate }}
... ...
... ... @@ -31,11 +31,15 @@ function GatewayForm() {
31 31 scope: true,
32 32 bindToController: {
33 33 formId: '=',
34   - ctx: '='
  34 + ctx: '=',
  35 + isStateForm: '=',
  36 + deviceName: '=',
  37 + isReadOnly: '=',
  38 + isState: '='
35 39 },
36 40 controller: GatewayFormController,
37 41 controllerAs: 'vm',
38   - templateUrl: gatewayFormTemplate
  42 + templateUrl: gatewayFormTemplate,
39 43 };
40 44 }
41 45
... ... @@ -47,10 +51,11 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
47 51 const configurationAttribute = "configuration";
48 52 const remoteLoggingLevelAttribute = "RemoteLoggingLevel";
49 53
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"';
  54 + 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=converterHandler}}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 55
52 56 vm.types = types;
53   -
  57 + vm.deviceNameForm = (vm.deviceName) ? vm.deviceName : null;
  58 + vm.idForm = Math.random().toString(36).replace(/^0\.[0-9]*/, '');
54 59 vm.configurations = {
55 60 gateway: '',
56 61 host: $document[0].domain,
... ... @@ -74,7 +79,6 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
74 79 let archiveFileName = '';
75 80 let gatewayNameExists = '';
76 81 let successfulSaved = '';
77   -
78 82 vm.securityTypes = [{
79 83 name: 'gateway.security-types.access-token',
80 84 value: 'accessToken'
... ... @@ -92,13 +96,18 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
92 96 }];
93 97
94 98 $scope.$watch('vm.ctx', function () {
95   - if (vm.ctx ) {
  99 + if (vm.ctx) {
  100 + vm.isStateForm = $scope.isStateForm;
96 101 vm.settings = vm.ctx.settings;
97 102 vm.widgetConfig = vm.ctx.widgetConfig;
98   - initializeConfig();
  103 + if (vm.ctx.datasources && vm.ctx.datasources.length) {
  104 + vm.deviceNameForm = vm.ctx.datasources[0].name;
  105 + }
99 106 }
  107 + initializeConfig();
100 108 });
101 109
  110 +
102 111 $scope.$on('gateway-form-resize', function (event, formId) {
103 112 if (vm.formId == formId) {
104 113 updateWidgetDisplaying();
... ... @@ -106,21 +115,34 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
106 115 });
107 116
108 117 function updateWidgetDisplaying() {
109   - vm.changeAlignment = (vm.ctx.$container[0].offsetWidth <= 425);
  118 + if (vm.ctx) {
  119 + vm.changeAlignment = (vm.ctx.$container[0].offsetWidth <= 425);
  120 + }
110 121 }
111 122
112 123 function initWidgetSettings() {
113 124 let widgetTitle;
114   - if (vm.settings.widgetTitle && vm.settings.widgetTitle.length) {
115   - widgetTitle = utils.customTranslation(vm.settings.widgetTitle, vm.settings.widgetTitle);
  125 + if (vm.settings) {
  126 + vm.isReadOnlyForm = (vm.settings.readOnly) ? vm.settings.readOnly : false;
  127 + if (vm.settings.gatewayTitle && vm.settings.gatewayTitle.length) {
  128 + widgetTitle = utils.customTranslation(vm.settings.gatewayTitle, vm.settings.gatewayTitle);
  129 + }
116 130 } else {
  131 + vm.isReadOnlyForm = false;
117 132 widgetTitle = $translate.instant('gateway.gateway');
118 133 }
119   - vm.ctx.widgetTitle = widgetTitle;
  134 + if (vm.ctx) {
  135 + vm.ctx.widgetTitle = widgetTitle;
  136 + }
120 137
121   - archiveFileName = vm.settings.archiveFileName && vm.settings.archiveFileName.length ? vm.settings.archiveFileName : 'gatewayConfiguration';
122   - gatewayNameExists = utils.customTranslation(vm.settings.gatewayNameExists, vm.settings.gatewayNameExists) || $translate.instant('gateway.gateway-exists');
123   - successfulSaved = utils.customTranslation(vm.settings.successfulSave, vm.settings.successfulSave) || $translate.instant('gateway.gateway-saved');
  138 + archiveFileName = vm.settings && vm.settings.archiveFileName && vm.settings.archiveFileName.length ? vm.settings.archiveFileName : 'gatewayConfiguration';
  139 + if (vm.settings) {
  140 + gatewayNameExists = utils.customTranslation(vm.settings.deviceNameExist, vm.settings.deviceNameExist) || $translate.instant('gateway.gateway-exists');
  141 + successfulSaved = utils.customTranslation(vm.settings.successfulSave, vm.settings.successfulSave) || $translate.instant('gateway.gateway-saved');
  142 + } else {
  143 + gatewayNameExists = $translate.instant('gateway.gateway-exists');
  144 + successfulSaved = $translate.instant('gateway.gateway-saved');
  145 + }
124 146 }
125 147
126 148 function initializeConfig() {
... ... @@ -129,6 +151,7 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
129 151 getGatewaysList(true);
130 152 }
131 153
  154 +
132 155 vm.getAccessToken = (deviceId) => {
133 156 if (deviceId.id) {
134 157 getDeviceCredentials(deviceId.id);
... ... @@ -152,15 +175,15 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
152 175 deviceService.findByName(deviceObj.name, {ignoreErrors: true})
153 176 .then(
154 177 function () {
155   - toast.showError(gatewayNameExists, angular.element('.gateway-form'),'top left');
  178 + toast.showError(gatewayNameExists, angular.element('.gateway-form'), 'top left');
156 179 },
157 180 function () {
158   - if(vm.settings.gatewayType && vm.settings.gatewayType.length){
  181 + if (vm.settings.gatewayType && vm.settings.gatewayType.length) {
159 182 deviceObj.type = vm.settings.gatewayType;
160 183 }
161 184 deviceService.saveDevice(deviceObj).then(
162 185 (device) => {
163   - getDeviceCredentials(device.id.id).then(() =>{
  186 + getDeviceCredentials(device.id.id).then(() => {
164 187 getGatewaysList();
165 188 });
166 189 }
... ... @@ -173,8 +196,8 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
173 196 saveAttribute(configurationAttribute, $window.btoa(angular.toJson(getGatewayConfigJSON())), types.attributesScope.shared.value),
174 197 saveAttribute(configurationDraftsAttribute, $window.btoa(angular.toJson(getDraftConnectorJSON())), types.attributesScope.server.value),
175 198 saveAttribute(remoteLoggingLevelAttribute, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value)
176   - ]).then(() =>{
177   - toast.showSuccess(successfulSaved, 2000, angular.element('.gateway-form'),'top left');
  199 + ]).then(() => {
  200 + toast.showSuccess(successfulSaved, 2000, angular.element('.gateway-form'), 'top left');
178 201 })
179 202 };
180 203
... ... @@ -238,7 +261,7 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
238 261 config += ' max_records_per_file: ' + vm.configurations.maxRecordsCount + '\n';
239 262 }
240 263 config += 'connectors:\n';
241   - for(let i = 0; i < vm.configurations.connectors.length; i++){
  264 + for (let i = 0; i < vm.configurations.connectors.length; i++) {
242 265 if (vm.configurations.connectors[i].enabled) {
243 266 config += ' -\n';
244 267 config += ' name: ' + vm.configurations.connectors[i].name + '\n';
... ... @@ -250,7 +273,7 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
250 273 }
251 274
252 275 function generateConfigConnectorFiles(fileZipAdd) {
253   - for(let i = 0; i < vm.configurations.connectors.length; i++){
  276 + for (let i = 0; i < vm.configurations.connectors.length; i++) {
254 277 if (vm.configurations.connectors[i].enabled) {
255 278 fileZipAdd[generateFileName(vm.configurations.connectors[i].name)] = angular.toJson(vm.configurations.connectors[i].config);
256 279 }
... ... @@ -276,7 +299,7 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
276 299
277 300 function gatewayMainConfigJSON() {
278 301 let configuration = {};
279   -
  302 +
280 303 let thingsBoard = {};
281 304 thingsBoard.host = vm.configurations.host;
282 305 thingsBoard.remoteConfiguration = vm.configurations.remoteConfiguration;
... ... @@ -325,10 +348,10 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
325 348 }
326 349
327 350 function gatewayConnectorConfigJSON(gatewayConfiguration) {
328   - for(let i = 0; i < vm.configurations.connectors.length; i++){
  351 + for (let i = 0; i < vm.configurations.connectors.length; i++) {
329 352 if (vm.configurations.connectors[i].enabled) {
330 353 let typeConnector = vm.configurations.connectors[i].configType;
331   - if(!angular.isArray(gatewayConfiguration[typeConnector])){
  354 + if (!angular.isArray(gatewayConfiguration[typeConnector])) {
332 355 gatewayConfiguration[typeConnector] = [];
333 356 }
334 357
... ... @@ -343,7 +366,7 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
343 366
344 367 function getDraftConnectorJSON() {
345 368 let draftConnector = {};
346   - for(let i = 0; i < vm.configurations.connectors.length; i++){
  369 + for (let i = 0; i < vm.configurations.connectors.length; i++) {
347 370 if (!vm.configurations.connectors[i].enabled) {
348 371 let connector = {
349 372 connector: vm.configurations.connectors[i].configType,
... ... @@ -362,7 +385,10 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
362 385 const device = devices[i];
363 386 if (device.additionalInfo !== null && device.additionalInfo.gateway === true) {
364 387 vm.gateways.push(device);
365   - if (firstInit && vm.gateways.length && device.name === vm.gateways[0].name) {
  388 + if (vm.deviceNameForm && firstInit && vm.gateways.length && device.name === vm.deviceNameForm) {
  389 + vm.configurations.gateway = device;
  390 + vm.getAccessToken(device.id);
  391 + } else if (firstInit && vm.gateways.length && device.name === vm.gateways[0].name) {
366 392 vm.configurations.gateway = device;
367 393 vm.getAccessToken(device.id);
368 394 }
... ... @@ -385,7 +411,7 @@ function GatewayFormController($scope, $injector, $document, $mdExpansionPanel,
385 411 for (let connectorType in keyValue) {
386 412 let name = "No name";
387 413 if (Object.prototype.hasOwnProperty.call(keyValue[connectorType], 'name')) {
388   - name = keyValue[connectorType].name ;
  414 + name = keyValue[connectorType].name;
389 415 }
390 416 let connector = {
391 417 enabled: true,
... ...
... ... @@ -17,30 +17,36 @@
17 17 -->
18 18 <md-content md-scroll-y layout="column" class="gateway-form">
19 19 <form name="gatewayConfiguration">
20   - <md-expansion-panel-group>
21   - <md-expansion-panel md-component-id="thingsboardPanelId">
  20 + <md-expansion-panel-group>
  21 + <md-expansion-panel md-component-id="{{vm.idForm}}thingsboardPanelId">
22 22 <md-expansion-panel-collapsed>
23 23 <div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div>
24 24 <span flex></span>
25 25 <md-expansion-panel-icon></md-expansion-panel-icon>
26 26 </md-expansion-panel-collapsed>
27 27 <md-expansion-panel-expanded>
28   - <md-expansion-panel-header ng-click="vm.collapsePanel('thingsboardPanelId')">
  28 + <md-expansion-panel-header ng-click="vm.collapsePanel(vm.idForm + 'thingsboardPanelId')">
29 29 <div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div>
30 30 <span flex></span>
31 31 <md-expansion-panel-icon></md-expansion-panel-icon>
32 32 </md-expansion-panel-header>
33 33 <md-expansion-panel-content>
34   - <tb-gateway-config-select tb-required="true"
  34 + <tb-gateway-config-select ng-if="!vm.isStateForm"
  35 + tb-required="true"
35 36 ng-model="vm.configurations.gateway"
36 37 the-form="gatewayConfiguration"
37 38 gateway-list="vm.gateways"
38 39 get_access_token="vm.getAccessToken"
39 40 create-device="vm.createDevice">
40 41 </tb-gateway-config-select>
  42 + <md-input-container ng-if="vm.isStateForm"
  43 + class="md-block">
  44 + <label>{{'gateway.gateway-name' | translate }}</label>
  45 + <input ng-disabled="true" type="text" ng-model="vm.configurations.gateway.name" name="gatewayName"/>
  46 + </md-input-container>
41 47 <md-input-container class="md-block">
42 48 <label>{{'gateway.security-type' | translate }}</label>
43   - <md-select name="securityType" ng-model="vm.configurations.securityType" required>
  49 + <md-select ng-disabled="vm.isReadOnlyForm" name="securityType" ng-model="vm.configurations.securityType" required>
44 50 <md-option ng-repeat="securityType in vm.securityTypes" ng-value="securityType.value">
45 51 {{securityType.name | translate}}
46 52 </md-option>
... ... @@ -50,14 +56,14 @@
50 56 ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
51 57 <md-input-container class="md-block" flex>
52 58 <label>{{ 'gateway.thingsboard-host' | translate }}</label>
53   - <input type="text" name="host" ng-model="vm.configurations.host" required>
  59 + <input ng-disabled="vm.isReadOnlyForm" type="text" name="host" ng-model="vm.configurations.host" required>
54 60 <div ng-messages="gatewayConfiguration.host.$error">
55 61 <div ng-message="required" translate>gateway.thingsboard-host-required</div>
56 62 </div>
57 63 </md-input-container>
58 64 <md-input-container class="md-block" flex>
59 65 <label>{{ 'gateway.thingsboard-port' | translate }}</label>
60   - <input type="number" min="1" max="65535" ng-pattern="/^-?[0-9]+$/" name="port"
  66 + <input ng-disabled="vm.isReadOnlyForm" type="number" min="1" max="65535" ng-pattern="/^-?[0-9]+$/" name="port"
61 67 ng-model="vm.configurations.port" required>
62 68 <div ng-messages="gatewayConfiguration.port.$error">
63 69 <div ng-message="required" translate>gateway.thingsboard-port-required</div>
... ... @@ -70,18 +76,18 @@
70 76 <div ng-if="vm.configurations.securityType=='tls'">
71 77 <md-input-container class="md-block security-type">
72 78 <label>{{'gateway.tls-path-ca-certificate' | translate }}</label>
73   - <input type="text" ng-model="vm.configurations.caCertPath" name="caCertPath" />
  79 + <input ng-disabled="vm.isReadOnlyForm" type="text" ng-model="vm.configurations.caCertPath" name="caCertPath"/>
74 80 </md-input-container>
75 81 <md-input-container class="md-block">
76 82 <label>{{'gateway.tls-path-private-key' | translate }}</label>
77   - <input type="text" ng-model="vm.configurations.privateKeyPath" name="privateKeyPath" />
  83 + <input ng-disabled="vm.isReadOnlyForm" type="text" ng-model="vm.configurations.privateKeyPath" name="privateKeyPath"/>
78 84 </md-input-container>
79 85 <md-input-container class="md-block">
80 86 <label>{{'gateway.tls-path-client-certificate' | translate }}</label>
81   - <input type="text" ng-model="vm.configurations.certPath" name="certPath" />
  87 + <input ng-disabled="vm.isReadOnlyForm" type="text" ng-model="vm.configurations.certPath" name="certPath"/>
82 88 </md-input-container>
83 89 </div>
84   - <md-checkbox ng-model="vm.configurations.remoteConfiguration"
  90 + <md-checkbox ng-show="!vm.isReadOnlyForm" ng-model="vm.configurations.remoteConfiguration"
85 91 name="remoteConfiguration"
86 92 aria-label="{{ 'gateway.remote-remote' | translate }}">
87 93 {{ 'gateway.remote' | translate }}
... ... @@ -90,7 +96,7 @@
90 96 ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
91 97 <md-input-container class="md-block md-select-container" flex>
92 98 <label>{{'gateway.remote-logging-level' | translate }}</label>
93   - <md-select name="loggingLevel" ng-model="vm.configurations.remoteLoggingLevel">
  99 + <md-select ng-disabled="vm.isReadOnlyForm" name="loggingLevel" ng-model="vm.configurations.remoteLoggingLevel">
94 100 <md-option ng-repeat="logLevel in vm.types.gatewayLogLevel"
95 101 ng-value="logLevel" ng-selected="$index === 5">
96 102 {{logLevel}}
... ... @@ -99,7 +105,7 @@
99 105 </md-input-container>
100 106 <md-input-container class="md-block" flex>
101 107 <label>{{'gateway.path-logs' | translate }}</label>
102   - <input type="text" ng-model="vm.configurations.remoteLoggingPathToLogs"
  108 + <input ng-disabled="vm.isReadOnlyForm" type="text" ng-model="vm.configurations.remoteLoggingPathToLogs"
103 109 name="remoteLoggingPathToLogs" required>
104 110 <div ng-messages="gatewayConfiguration.remoteLoggingPathToLogs.$error">
105 111 <div ng-message="required" translate>gateway.path-logs-required</div>
... ... @@ -109,14 +115,14 @@
109 115 </md-expansion-panel-content>
110 116 </md-expansion-panel-expanded>
111 117 </md-expansion-panel>
112   - <md-expansion-panel md-component-id="storagePanelId">
  118 + <md-expansion-panel md-component-id="{{vm.idForm}}storagePanelId">
113 119 <md-expansion-panel-collapsed>
114 120 <div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div>
115 121 <span flex></span>
116 122 <md-expansion-panel-icon></md-expansion-panel-icon>
117 123 </md-expansion-panel-collapsed>
118 124 <md-expansion-panel-expanded>
119   - <md-expansion-panel-header ng-click="vm.collapsePanel('storagePanelId')">
  125 + <md-expansion-panel-header ng-click="vm.collapsePanel(vm.idForm + 'storagePanelId')">
120 126 <div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div>
121 127 <span flex></span>
122 128 <md-expansion-panel-icon></md-expansion-panel-icon>
... ... @@ -124,18 +130,17 @@
124 130 <md-expansion-panel-content>
125 131 <md-input-container class="md-block" flex>
126 132 <label>{{'gateway.storage-type' | translate }}</label>
127   - <md-select required ng-model="vm.configurations.storageType">
  133 + <md-select ng-disabled="vm.isReadOnlyForm" required ng-model="vm.configurations.storageType">
128 134 <md-option ng-repeat="storageType in vm.storageTypes" ng-value="storageType.value">
129 135 {{storageType.name | translate}}
130 136 </md-option>
131 137 </md-select>
132 138 </md-input-container>
133   -
134 139 <div layout="row" class="gateway-form-row"
135 140 ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
136 141 <md-input-container class="md-block" flex>
137 142 <label>{{'gateway.storage-pack-size' | translate }}</label>
138   - <input type="number" min="1" name="readRecordsCount" ng-pattern="/^-?[0-9]+$/"
  143 + <input ng-disabled="vm.isReadOnlyForm" type="number" min="1" name="readRecordsCount" ng-pattern="/^-?[0-9]+$/"
139 144 ng-model='vm.configurations.readRecordsCount' required/>
140 145 <div ng-messages="gatewayConfiguration.readRecordsCount.$error">
141 146 <div ng-message="required" translate>gateway.storage-pack-size-required</div>
... ... @@ -143,10 +148,9 @@
143 148 <div ng-message="pattern" translate>gateway.storage-pack-size-pattern</div>
144 149 </div>
145 150 </md-input-container>
146   -
147 151 <md-input-container class="md-block" flex>
148 152 <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]+$/"
  153 + <input ng-disabled="vm.isReadOnlyForm" type="number" min="1" name="maxRecordsCount" ng-pattern="/^-?[0-9]+$/"
150 154 ng-model='vm.configurations.maxRecordsCount' required/>
151 155 <div ng-messages="gatewayConfiguration.maxRecordsCount.$error">
152 156 <div ng-message="required" translate>gateway.storage-max-records-required</div>
... ... @@ -155,13 +159,12 @@
155 159 </div>
156 160 </md-input-container>
157 161 </div>
158   -
159 162 <div layout="row" class="gateway-form-row"
160 163 ng-if="vm.configurations.storageType == 'fileStorage'"
161 164 ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
162 165 <md-input-container class="md-block" flex>
163 166 <label>{{'gateway.storage-max-files' | translate }}</label>
164   - <input type="number" min="1" name="maxFilesCount" ng-pattern="/^-?[0-9]+$/"
  167 + <input ng-disabled="vm.isReadOnlyForm" type="number" min="1" name="maxFilesCount" ng-pattern="/^-?[0-9]+$/"
165 168 ng-model='vm.configurations.maxFilesCount' required/>
166 169 <div ng-messages="gatewayConfiguration.maxFilesCount.$error">
167 170 <div ng-message="required" translate>gateway.storage-max-files-required</div>
... ... @@ -169,10 +172,9 @@
169 172 <div ng-message="pattern" translate>gateway.storage-max-files-pattern</div>
170 173 </div>
171 174 </md-input-container>
172   -
173 175 <md-input-container class="md-block" flex>
174 176 <label>{{'gateway.storage-path' | translate }}</label>
175   - <input type="text" name="dataFolderPath" ng-model='vm.configurations.dataFolderPath'
  177 + <input ng-disabled="vm.isReadOnlyForm" type="text" name="dataFolderPath" ng-model='vm.configurations.dataFolderPath'
176 178 required/>
177 179 <div ng-messages="gatewayConfiguration.dataFolderPath.$error">
178 180 <div ng-message="required" translate>gateway.storage-path-required</div>
... ... @@ -182,14 +184,14 @@
182 184 </md-expansion-panel-content>
183 185 </md-expansion-panel-expanded>
184 186 </md-expansion-panel>
185   - <md-expansion-panel md-component-id="connectorsPanelId">
  187 + <md-expansion-panel md-component-id="{{vm.idForm}}connectorsPanelId">
186 188 <md-expansion-panel-collapsed>
187 189 <div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div>
188 190 <span flex></span>
189 191 <md-expansion-panel-icon></md-expansion-panel-icon>
190 192 </md-expansion-panel-collapsed>
191 193 <md-expansion-panel-expanded>
192   - <md-expansion-panel-header ng-click="vm.collapsePanel('connectorsPanelId')">
  194 + <md-expansion-panel-header ng-click="vm.collapsePanel(vm.idForm + 'connectorsPanelId')">
193 195 <div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div>
194 196 <span flex></span>
195 197 <md-expansion-panel-icon></md-expansion-panel-icon>
... ... @@ -198,13 +200,14 @@
198 200 <tb-gateway-config
199 201 gateway-config="vm.configurations.connectors"
200 202 the-form="gatewayConfiguration"
201   - change-alignment="vm.changeAlignment">
  203 + change-alignment="vm.changeAlignment"
  204 + is-read-only="vm.isReadOnlyForm">
202 205 </tb-gateway-config>
203 206 </md-expansion-panel-content>
204 207 </md-expansion-panel-expanded>
205 208 </md-expansion-panel>
206 209 </md-expansion-panel-group>
207   - <section layout="row" layout-align="end center" class="form-action-buttons">
  210 + <section ng-show="!vm.isReadOnlyForm" layout="row" layout-align="end center" class="form-action-buttons">
208 211 <md-button class="md-primary md-raised"
209 212 ng-click="vm.exportConfig()"
210 213 ng-if="!vm.configurations.remoteConfiguration"
... ... @@ -213,14 +216,14 @@
213 216 {{'action.download' | translate }}
214 217 <md-tooltip>{{'gateway.download-tip' | translate }}</md-tooltip>
215 218 </md-button>
216   -
217 219 <md-button class="md-primary md-raised"
218 220 ng-click="vm.saveAttributeConfig()"
219 221 ng-if="vm.configurations.remoteConfiguration"
220 222 ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty"
221 223 aria-label="{{ 'gateway.save-tip' | translate }}">
222 224 {{'action.save' | translate }}
223   - <md-tooltip ng-if="vm.configurations.remoteConfiguration">{{'gateway.save-tip' | translate }}</md-tooltip>
  225 + <md-tooltip ng-if="vm.configurations.remoteConfiguration">{{'gateway.save-tip' | translate }}
  226 + </md-tooltip>
224 227 </md-button>
225 228 </section>
226 229 </form>
... ...
... ... @@ -1201,7 +1201,12 @@
1201 1201 "tls-path-private-key": "Path to private key on gateway",
1202 1202 "toggle-fullscreen": "Toggle fullscreen",
1203 1203 "transformer-json-config": "Configuration JSON*",
1204   - "update-config": "Add/update configuration JSON"
  1204 + "update-config": "Add/update configuration JSON",
  1205 + "state-title": "Gateway state",
  1206 + "show-config-tip": "Show gateway configuration",
  1207 + "title-show-config": "Show gateway configuration",
  1208 + "read-only": "Read only",
  1209 + "read-write": ""
1205 1210 },
1206 1211 "grid": {
1207 1212 "delete-item-title": "Are you sure you want to delete this item?",
... ...