WIP_Gate way form (#2370)
* gateWayForm: start branch * gateWayForm: start branch2 * gateWayForm: start add new form to gateway_widgets.json * gateWayForm: start add new logs.conf * Fix html and clear js * gateWayForm: start add new222 * improvement gateway config form (change html) * gateWayForm: new vadim verstka * GatewayForm: add valid config * GatewayForm: add valid config compile and add form to widgets library * GatewayForm: bug err yml Co-authored-by: Vladyslav <vprykhodko@thingsboard.io>
This commit is contained in:
parent
fbed56555f
commit
e3b39bedf6
@ -20,6 +20,26 @@
|
||||
"dataKeySettingsSchema": "{}\n",
|
||||
"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\":\"4px\",\"settings\":{},\"title\":\"Extensions table\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"18px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"alias": "new_config_form",
|
||||
"name": "Config form",
|
||||
"descriptor": {
|
||||
"type": "static",
|
||||
"sizeX": 7.5,
|
||||
"sizeY": 10.5,
|
||||
"resources": [
|
||||
{
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"templateHtml": "<tb-gateway-form\n form-id=\"formId\"\n ctx=\"ctx\">\n</tb-gateway-form>\n",
|
||||
"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/*}*/",
|
||||
"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",
|
||||
"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",
|
||||
"dataKeySettingsSchema": "{}\n",
|
||||
"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\":{}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -61,6 +61,7 @@
|
||||
"js-beautify": "^1.10.0",
|
||||
"json-schema-defaults": "^0.2.0",
|
||||
"jstree": "^3.3.8",
|
||||
"jszip": "^3.2.2",
|
||||
"jstree-bootstrap-theme": "^1.0.1",
|
||||
"leaflet": "^1.5.1",
|
||||
"leaflet-polylinedecorator": "^1.6.0",
|
||||
|
||||
@ -584,6 +584,32 @@ export default angular.module('thingsboard.types', [])
|
||||
opc: "OPC UA",
|
||||
modbus: "MODBUS"
|
||||
},
|
||||
gatewayConfigType: {
|
||||
mqtt: {
|
||||
value: "mqtt",
|
||||
name: "MQTT"
|
||||
},
|
||||
modbus: {
|
||||
value: "modbus",
|
||||
name: "Modbus"
|
||||
},
|
||||
opc_ua: {
|
||||
value: "opcua",
|
||||
name: "OPC-UA"
|
||||
},
|
||||
ble: {
|
||||
value: "ble",
|
||||
name: "BLE"
|
||||
}
|
||||
},
|
||||
gatewayLogLevel: {
|
||||
none: "NONE",
|
||||
critical: "CRITICAL",
|
||||
error: "ERROR",
|
||||
warning: "WARNING",
|
||||
info: "INFO",
|
||||
debug: "DEBUG"
|
||||
},
|
||||
extensionValueType: {
|
||||
string: 'value.string',
|
||||
long: 'value.long',
|
||||
|
||||
75
ui/src/app/components/gateWay/gateway-config-dialog.tpl.html
Normal file
75
ui/src/app/components/gateWay/gateway-config-dialog.tpl.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2020 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<md-dialog class="gateway-config-dialog">
|
||||
<form name="theForm" ng-submit="vm.save()">
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h2 translate translate-values='{ typeName: "{{vm.typeName | translate}}" }'>
|
||||
gateway.title-connectors-json
|
||||
</h2>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button"
|
||||
ng-click="vm.cancel()"
|
||||
aria-label="{{'action.close'|translate}}">
|
||||
<ng-md-icon icon="close" aria-label="Close"></ng-md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-dialog-content>
|
||||
<div class="md-dialog-content">
|
||||
<div tb-expand-fullscreen fullscreen-zindex="100" expand-button-id="expand-button" layout="column">
|
||||
<div layout="row" layout-align="start center" class="tb-json-toolbar">
|
||||
<label class="tb-title no-padding" translate>gateway.transformer-json-config</label>
|
||||
<span flex></span>
|
||||
<md-button ng-if="!readonly"
|
||||
aria-label="{{'gateway.tidy'|translate}}"
|
||||
class="tidy"
|
||||
ng-click="vm.beautifyJson()">
|
||||
{{'gateway.tidy'|translate}}
|
||||
<md-tooltip md-direction="top">{{'gateway.tidy-tip' | translate }}</md-tooltip>
|
||||
</md-button>
|
||||
<md-button id="expand-button" aria-label="Fullscreen"
|
||||
class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button>
|
||||
</div>
|
||||
<div layout="row" class="tb-json-panel" flex>
|
||||
<div flex class="tb-json-input"
|
||||
ui-ace="vm.configAreaOptions"
|
||||
ng-model="vm.config"
|
||||
name="config"
|
||||
ng-change='vm.validateConfig(vm.config, "config")'
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-error-messages" layout="column" flex ng-messages="theForm.config.$error" role="alert">
|
||||
<div ng-message="required" flex class="tb-error-message" translate>gateway.json-required</div>
|
||||
<div ng-message="vm.config" class="tb-error-message" translate>gateway.json-parse</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</md-dialog-content>
|
||||
<md-dialog-actions layout="row" layout-align="end center" class="action-buttons">
|
||||
<md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
|
||||
class="md-raised md-primary">
|
||||
{{'action.save'|translate}}
|
||||
</md-button>
|
||||
<md-button id="cancel-btn" ng-disabled="$root.loading" ng-click="vm.cancel()">
|
||||
{{'action.cancel'|translate }}
|
||||
</md-button>
|
||||
</md-dialog-actions>
|
||||
</form>
|
||||
</md-dialog>
|
||||
137
ui/src/app/components/gateWay/gateway-config-select.directive.js
Normal file
137
ui/src/app/components/gateWay/gateway-config-select.directive.js
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import './gateway-config-select.scss';
|
||||
|
||||
/* eslint-disable import/no-unresolved, import/default */
|
||||
|
||||
import gatewayAliasSelectTemplate from './gateway-config-select.tpl.html';
|
||||
|
||||
/* eslint-enable import/no-unresolved, import/default */
|
||||
|
||||
|
||||
/* eslint-disable angular/angularelement */
|
||||
|
||||
export default angular.module('thingsboard.directives.gatewayConfigSelect', [])
|
||||
.directive('tbGatewayConfigSelect', GatewayConfigSelect)
|
||||
.name;
|
||||
|
||||
/*@ngInject*/
|
||||
function GatewayConfigSelect($compile, $templateCache, $mdConstant, $translate, $mdDialog) {
|
||||
|
||||
var linker = function (scope, element, attrs, ngModelCtrl) {
|
||||
var template = $templateCache.get(gatewayAliasSelectTemplate);
|
||||
element.html(template);
|
||||
|
||||
scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
|
||||
|
||||
scope.ngModelCtrl = ngModelCtrl;
|
||||
scope.singleSelect = null;
|
||||
|
||||
scope.updateValidity = function () {
|
||||
var value = ngModelCtrl.$viewValue;
|
||||
var valid = angular.isDefined(value) && value != null || !scope.tbRequired;
|
||||
ngModelCtrl.$setValidity('singleSelect', valid);
|
||||
};
|
||||
|
||||
scope.$watch('singleSelect', function () {
|
||||
scope.updateView();
|
||||
});
|
||||
|
||||
scope.gatewayNameSearch = function (gatewaySearchText) {
|
||||
return gatewaySearchText ? scope.gatewayList.filter(
|
||||
scope.createFilterForGatewayName(gatewaySearchText)) : scope.gatewayList;
|
||||
};
|
||||
|
||||
scope.createFilterForGatewayName = function (query) {
|
||||
var lowercaseQuery = query.toLowerCase();
|
||||
return function filterFn(device) {
|
||||
return (device.toLowerCase().indexOf(lowercaseQuery) === 0);
|
||||
};
|
||||
};
|
||||
|
||||
scope.updateView = function () {
|
||||
ngModelCtrl.$setViewValue(scope.singleSelect);
|
||||
scope.updateValidity();
|
||||
let deviceObj = {"name": scope.singleSelect, "type": "Gateway", "additionalInfo": {
|
||||
"gateway": true
|
||||
}};
|
||||
scope.getAccessToken(deviceObj);
|
||||
};
|
||||
|
||||
ngModelCtrl.$render = function () {
|
||||
if (ngModelCtrl.$viewValue) {
|
||||
scope.singleSelect = ngModelCtrl.$viewValue;
|
||||
}
|
||||
};
|
||||
|
||||
scope.textIsEmpty = function (str) {
|
||||
return (!str || 0 === str.length);
|
||||
};
|
||||
|
||||
scope.gatewayNameEnter = function ($event) {
|
||||
if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) {
|
||||
$event.preventDefault();
|
||||
let indexRes = scope.gatewayList.findIndex((element) => element.key === scope.gatewaySearchText);
|
||||
if (indexRes === -1) {
|
||||
scope.createNewGatewayDialog($event, {name: scope.gatewaySearchText});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scope.createNewGatewayDialog = function ($event, deviceName) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
var title = $translate.instant('gateway.create-new-gateway');
|
||||
var content = $translate.instant('gateway.create-new-gateway-text', {gatewayName: deviceName.name});
|
||||
var confirm = $mdDialog.confirm()
|
||||
.targetEvent($event)
|
||||
.title(title)
|
||||
.htmlContent(content)
|
||||
.ariaLabel(title)
|
||||
.cancel($translate.instant('action.no'))
|
||||
.ok($translate.instant('action.yes'));
|
||||
$mdDialog.show(confirm).then(
|
||||
() => {
|
||||
let deviceObj = {"name": deviceName.name, "type": "Gateway", "additionalInfo": {
|
||||
"gateway": true
|
||||
}};
|
||||
scope.createDevice(deviceObj);
|
||||
},
|
||||
() => {
|
||||
scope.gatewaySearchText = "";
|
||||
}
|
||||
);
|
||||
};
|
||||
$compile(element.contents())(scope);
|
||||
};
|
||||
|
||||
return {
|
||||
restrict: "E",
|
||||
require: "^ngModel",
|
||||
link: linker,
|
||||
scope: {
|
||||
tbRequired: '=?',
|
||||
allowedEntityTypes: '=?',
|
||||
gatewayList: '=?',
|
||||
getAccessToken: '=',
|
||||
createDevice: '=',
|
||||
theForm: '='
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-enable angular/angularelement */
|
||||
35
ui/src/app/components/gateWay/gateway-config-select.scss
Normal file
35
ui/src/app/components/gateWay/gateway-config-select.scss
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.tb-gateway-autocomplete {
|
||||
.tb-not-found {
|
||||
line-height: 1.5;
|
||||
white-space: normal;
|
||||
|
||||
.tb-no-gateway {
|
||||
line-height: 48px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tb-gateway-autocomplete-container.md-virtual-repeat-container.md-autocomplete-suggestions-container{
|
||||
z-index: 70;
|
||||
}
|
||||
|
||||
md-autocomplete{
|
||||
md-input-container{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
54
ui/src/app/components/gateWay/gateway-config-select.tpl.html
Normal file
54
ui/src/app/components/gateWay/gateway-config-select.tpl.html
Normal file
@ -0,0 +1,54 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2020 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<section layout='column'>
|
||||
<md-autocomplete md-input-name="singleSelect" flex
|
||||
ng-required="tbRequired"
|
||||
md-no-cache="true"
|
||||
ng-model="singleSelect"
|
||||
md-selected-item="singleSelect"
|
||||
md-search-text="gatewaySearchText"
|
||||
md-items="item in gatewayNameSearch(gatewaySearchText)"
|
||||
md-item-text="item"
|
||||
tb-keydown="gatewayNameEnter($event)"
|
||||
tb-keypress="gatewayNameEnter($event)"
|
||||
md-min-length="0"
|
||||
md-clear-button="true"
|
||||
md-floating-label="{{ 'gateway.gateway-name' | translate }}"
|
||||
md-menu-class="tb-gateway-autocomplete"
|
||||
md-input-class="tb-test"
|
||||
md-menu-container-class="tb-gateway-autocomplete-container">
|
||||
<md-item-template>
|
||||
<span md-highlight-text="gatewaySearchText" md-highlight-flags="^i">{{item}}</span>
|
||||
</md-item-template>
|
||||
<md-not-found>
|
||||
<div class="tb-not-found">
|
||||
<div class="tb-no-gateway" ng-if="textIsEmpty(gatewaySearchText)">
|
||||
<span translate>gateway.no-gateway-found</span>
|
||||
</div>
|
||||
<div ng-if="!textIsEmpty(gatewaySearchText)">
|
||||
<span translate
|
||||
translate-values='{ item: "{{gatewaySearchText | truncate:true:6:'...'}}" }'>gateway.no-gateway-matching</span>
|
||||
<a translate ng-click="createNewGatewayDialog($event, {name: gatewaySearchText})">gateway.create-new-gateway</a>
|
||||
</div>
|
||||
</div>
|
||||
</md-not-found>
|
||||
<div ng-messages="theForm.singleSelect.$error">
|
||||
<div ng-message="required" translate>Test</div>
|
||||
</div>
|
||||
</md-autocomplete>
|
||||
</section>
|
||||
317
ui/src/app/components/gateWay/gateway-config.directive.js
Normal file
317
ui/src/app/components/gateWay/gateway-config.directive.js
Normal file
@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import './gateway-config.scss';
|
||||
|
||||
/* eslint-disable import/no-unresolved, import/default */
|
||||
|
||||
import gatewayTemplate from './gateway-config.tpl.html';
|
||||
import gatewayDialogTemplate from './gateway-config-dialog.tpl.html';
|
||||
import beautify from "js-beautify";
|
||||
|
||||
/* eslint-enable import/no-unresolved, import/default */
|
||||
const js_beautify = beautify.js;
|
||||
|
||||
export default angular.module('thingsboard.directives.gatewayConfig', [])
|
||||
.directive('tbGatewayConfig', GatewayConfig)
|
||||
.name;
|
||||
|
||||
/*@ngInject*/
|
||||
function GatewayConfig() {
|
||||
return {
|
||||
restrict: "E",
|
||||
scope: true,
|
||||
bindToController: {
|
||||
disabled: '=ngDisabled',
|
||||
titleText: '@?',
|
||||
keyPlaceholderText: '@?',
|
||||
valuePlaceholderText: '@?',
|
||||
noDataText: '@?',
|
||||
gatewayConfig: '=',
|
||||
changeAlignment: '='
|
||||
},
|
||||
controller: GatewayConfigController,
|
||||
controllerAs: 'vm',
|
||||
templateUrl: gatewayTemplate
|
||||
};
|
||||
}
|
||||
|
||||
/*@ngInject*/
|
||||
function GatewayConfigController($scope, $document, $mdDialog, $mdUtil, $window, types, toast, $timeout, $compile, $translate) { //eslint-disable-line
|
||||
|
||||
let vm = this;
|
||||
|
||||
vm.kvList = [];
|
||||
vm.types = types;
|
||||
$scope.$watch('vm.gatewayConfig', () => {
|
||||
vm.stopWatchKvList();
|
||||
vm.kvList.length = 0;
|
||||
if (vm.gatewayConfig) {
|
||||
for (var property in vm.gatewayConfig) {
|
||||
if (Object.prototype.hasOwnProperty.call(vm.gatewayConfig, property)) {
|
||||
vm.kvList.push(
|
||||
{
|
||||
enabled: vm.gatewayConfig[property].enabled,
|
||||
key: property + '',
|
||||
value: vm.gatewayConfig[property].connector + '',
|
||||
config: js_beautify(vm.gatewayConfig[property].config + '', {indent_size: 4})
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
$mdUtil.nextTick(() => {
|
||||
vm.watchKvList();
|
||||
});
|
||||
});
|
||||
|
||||
vm.watchKvList = () => {
|
||||
$scope.kvListWatcher = $scope.$watch('vm.kvList', () => {
|
||||
if (!vm.gatewayConfig) {
|
||||
return;
|
||||
}
|
||||
for (let property in vm.gatewayConfig) {
|
||||
if (Object.prototype.hasOwnProperty.call(vm.gatewayConfig, property)) {
|
||||
delete vm.gatewayConfig[property];
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < vm.kvList.length; i++) {
|
||||
let entry = vm.kvList[i];
|
||||
if (entry.key && entry.value) {
|
||||
let connectorJSON = angular.toJson({
|
||||
enabled: entry.enabled,
|
||||
connector: entry.value,
|
||||
config: angular.fromJson(entry.config)
|
||||
});
|
||||
vm.gatewayConfig [entry.key] = angular.fromJson(connectorJSON);
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
};
|
||||
|
||||
vm.stopWatchKvList = () => {
|
||||
if ($scope.kvListWatcher) {
|
||||
$scope.kvListWatcher();
|
||||
$scope.kvListWatcher = null;
|
||||
}
|
||||
};
|
||||
|
||||
vm.removeKeyVal = (index) => {
|
||||
if (index > -1) {
|
||||
vm.kvList.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
vm.addKeyVal = () => {
|
||||
if (!vm.kvList) {
|
||||
vm.kvList = [];
|
||||
}
|
||||
vm.kvList.push(
|
||||
{
|
||||
enabled: false,
|
||||
key: '',
|
||||
value: '',
|
||||
config: '{}'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
vm.openConfigDialog = ($event, index, config, typeName) => {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
$mdDialog.show({
|
||||
controller: GatewayDialogController,
|
||||
controllerAs: 'vm',
|
||||
templateUrl: gatewayDialogTemplate,
|
||||
parent: angular.element($document[0].body),
|
||||
locals: {
|
||||
config: config,
|
||||
typeName: typeName
|
||||
},
|
||||
targetEvent: $event,
|
||||
fullscreen: true,
|
||||
multiple: true,
|
||||
}).then(function (config) {
|
||||
if (config) {
|
||||
if (index > -1) {
|
||||
vm.kvList[index].config = config;
|
||||
}
|
||||
}
|
||||
}, function () {
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
vm.configTypeChange = (keyVal) => {
|
||||
for (let prop in types.gatewayConfigType) {
|
||||
if (types.gatewayConfigType[prop].value === keyVal.value) {
|
||||
if (!keyVal.key) {
|
||||
keyVal.key = vm.configTypeChangeValid(types.gatewayConfigType[prop].name, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
vm.checkboxValid(keyVal);
|
||||
};
|
||||
|
||||
vm.keyValChange = (keyVal, indexKey) => {
|
||||
keyVal.key = vm.keyValChangeValid(keyVal.key, 0, indexKey);
|
||||
vm.checkboxValid(keyVal);
|
||||
};
|
||||
|
||||
vm.configTypeChangeValid = (name, index) => {
|
||||
let newKeyName = index ? name + index : name;
|
||||
let indexRes = vm.kvList.findIndex((element) => element.key === newKeyName);
|
||||
return indexRes === -1 ? newKeyName : vm.configTypeChangeValid(name, ++index);
|
||||
};
|
||||
|
||||
vm.keyValChangeValid = (name, index, indexKey) => {
|
||||
angular.forEach(vm.kvList, function (value, key) {
|
||||
let nameEq = (index === 0) ? name : name + index;
|
||||
if (key !== indexKey && value.key && value.key === nameEq) {
|
||||
index++;
|
||||
vm.keyValChangeValid(name, index, indexKey);
|
||||
}
|
||||
|
||||
});
|
||||
return (index === 0) ? name : name + index;
|
||||
};
|
||||
|
||||
vm.buttonValid = (config) => {
|
||||
return (angular.equals("{}", config)) ? "md-warn" : "md-primary";
|
||||
};
|
||||
|
||||
vm.checkboxValid = (keyVal) => {
|
||||
if (!keyVal.key || angular.equals("", keyVal.key)
|
||||
|| !keyVal.value || angular.equals("", keyVal.value)
|
||||
|| angular.equals("{}", keyVal.config)) {
|
||||
return keyVal.enabled = false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
vm.checkboxValidMouseover = ($event, keyVal) => {
|
||||
console.log($event, keyVal); //eslint-disable-line
|
||||
vm.checkboxValidClick ($event, keyVal);
|
||||
};
|
||||
|
||||
vm.checkboxValidClick = ($event, keyVal) => {
|
||||
if (!vm.checkboxValid(keyVal)) {
|
||||
let errTxt = "";
|
||||
if (!keyVal.key || angular.equals("", keyVal.key)) {
|
||||
errTxt = $translate.instant('gateway.keyval-name-err');
|
||||
}
|
||||
|
||||
if (!keyVal.value || angular.equals("", keyVal.value)) {
|
||||
errTxt += '<div>' + $translate.instant('gateway.keyval-type-err') + '</div>';
|
||||
}
|
||||
|
||||
if (angular.equals("{}", keyVal.config)) {
|
||||
errTxt += '<div>' + $translate.instant('gateway.keyval-config-err') + '</div>';
|
||||
}
|
||||
if (!angular.equals("", errTxt)) {
|
||||
displayTooltip($event, '<div class="tb-rule-node-tooltip tb-lib-tooltip">' +
|
||||
'<div id="tb-node-content" layout="column">' +
|
||||
'<div class="tb-node-title">' + $translate.instant('gateway.keyval-save-err') + '</div>' +
|
||||
'<div class="tb-node-details">' + errTxt + '</div>' +
|
||||
'</div>' +
|
||||
'</div>');
|
||||
}
|
||||
}
|
||||
else {
|
||||
destroyTooltips();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function displayTooltip(event, content) {
|
||||
destroyTooltips();
|
||||
vm.tooltipTimeout = $timeout(() => {
|
||||
var element = angular.element(event.target);
|
||||
element.tooltipster(
|
||||
{
|
||||
theme: 'tooltipster-shadow',
|
||||
delay: 10,
|
||||
animation: 'grow',
|
||||
side: 'right'
|
||||
}
|
||||
);
|
||||
var contentElement = angular.element(content);
|
||||
$compile(contentElement)($scope);
|
||||
var tooltip = element.tooltipster('instance');
|
||||
tooltip.content(contentElement);
|
||||
tooltip.open();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function destroyTooltips() {
|
||||
if (vm.tooltipTimeout) {
|
||||
$timeout.cancel(vm.tooltipTimeout);
|
||||
vm.tooltipTimeout = null;
|
||||
}
|
||||
var instances = angular.element.tooltipster.instances();
|
||||
instances.forEach((instance) => {
|
||||
if (!instance.isErrorTooltip) {
|
||||
instance.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*@ngInject*/
|
||||
function GatewayDialogController($scope, $mdDialog, $document, $window, config, typeName) {
|
||||
let vm = this;
|
||||
vm.doc = $document[0];
|
||||
vm.config = angular.copy(config);
|
||||
vm.typeName = "" + typeName;
|
||||
vm.configAreaOptions = {
|
||||
useWrapMode: false,
|
||||
mode: 'json',
|
||||
showGutter: true,
|
||||
showPrintMargin: true,
|
||||
theme: 'github',
|
||||
advanced: {
|
||||
enableSnippets: true,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true
|
||||
},
|
||||
onLoad: function (_ace) {
|
||||
_ace.$blockScrolling = 1;
|
||||
}
|
||||
};
|
||||
|
||||
vm.validateConfig = (model, editorName) => {
|
||||
if (model && model.length) {
|
||||
try {
|
||||
angular.fromJson(model);
|
||||
$scope.theForm[editorName].$setValidity('configJSON', true);
|
||||
} catch (e) {
|
||||
$scope.theForm[editorName].$setValidity('configJSON', false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vm.save = () => {
|
||||
$mdDialog.hide(vm.config);
|
||||
};
|
||||
|
||||
vm.cancel = () => {
|
||||
$mdDialog.hide();
|
||||
};
|
||||
|
||||
vm.beautifyJson = () => {
|
||||
vm.config = js_beautify(vm.config, {indent_size: 4});
|
||||
};
|
||||
}
|
||||
|
||||
85
ui/src/app/components/gateWay/gateway-config.scss
Normal file
85
ui/src/app/components/gateWay/gateway-config.scss
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.gateway-config {
|
||||
span.no-data-found {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
text-transform: uppercase;
|
||||
|
||||
&.disabled {
|
||||
color: rgba(0, 0, 0, .38);
|
||||
}
|
||||
}
|
||||
|
||||
.gateway-config-row{
|
||||
md-input-container{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.gateway-config-row-vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons.gateway-config-row-vertical {
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
}
|
||||
|
||||
.gateway-config-dialog{
|
||||
.md-button.tidy{
|
||||
min-width: 32px;
|
||||
min-height: 15px;
|
||||
padding: 4px;
|
||||
margin: 0 5px 0 0;
|
||||
font-size: .8rem;
|
||||
line-height: 15px;
|
||||
color: #7b7b7b;
|
||||
background: rgba(220, 220, 220, .35);
|
||||
}
|
||||
|
||||
.tb-json-toolbar{
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.tb-json-panel {
|
||||
height: calc(100% - 80px);
|
||||
margin-left: 15px;
|
||||
border: 1px solid #c0c0c0;
|
||||
|
||||
.tb-json-input {
|
||||
width: 100%;
|
||||
min-width: 400px;
|
||||
height: 100%;
|
||||
|
||||
&:not(.fill-height) {
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 425px){
|
||||
.gateway-config-dialog{
|
||||
.tb-json-panel {
|
||||
.tb-json-input {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
ui/src/app/components/gateWay/gateway-config.tpl.html
Normal file
94
ui/src/app/components/gateWay/gateway-config.tpl.html
Normal file
@ -0,0 +1,94 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2020 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<form name="gatewayConfig" flex layout="column" class="gateway-config">
|
||||
<div layout="row" id="section-row" ng-repeat="keyVal in vm.kvList track by $index">
|
||||
<div layout="column" layout-align="center center" class="gateway-config-row">
|
||||
<md-input-container class="md-block">
|
||||
<md-checkbox ng-model="keyVal.enabled"
|
||||
aria-label="{{ 'gateway.enabled' | translate }}"
|
||||
ng-change="vm.checkboxValid(keyVal)"
|
||||
ng-click="vm.checkboxValidClick($event, keyVal)"
|
||||
ng-mouseover="vm.checkboxValidMouseover($event, keyVal)">
|
||||
</md-checkbox>
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'gateway.enabled' | translate }}
|
||||
</md-tooltip>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<div layout="row" flex class="gateway-config-row"
|
||||
ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{'gateway.connector-type' | translate }}</label>
|
||||
<md-select name="configType" ng-change="vm.configTypeChange(keyVal)" ng-model="keyVal.value" required>
|
||||
<md-option ng-repeat="configType in vm.types.gatewayConfigType" ng-value="configType.value">
|
||||
{{configType.value}}
|
||||
</md-option>
|
||||
</md-select>
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'gateway.connector-type' | translate }}
|
||||
</md-tooltip>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex>
|
||||
<input placeholder="{{ (vm.keyPlaceholderText ? vm.keyPlaceholderText : 'gateway.name') | translate }}"
|
||||
ng-model-options="{ updateOn: 'blur' }"
|
||||
ng-change="vm.keyValChange(keyVal, $index)" name="key" ng-model="keyVal.key" required/>
|
||||
<div ng-messages="gatewayConfig.key.$error">
|
||||
<div ng-message="required" translate>extension.field-required</div>
|
||||
</div>
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'gateway.name' | translate }}
|
||||
</md-tooltip>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<div layout="row" layout-align="end center" class="action-buttons"
|
||||
ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
|
||||
<md-button class="md-icon-button md-fab md-mini"
|
||||
name="updateconf"
|
||||
ng-click="vm.openConfigDialog($event, $index, keyVal.config, keyVal.key)"
|
||||
aria-label="{{ 'gateway.update-config' | translate }}"
|
||||
ng-class="vm.buttonValid(keyVal.config)" required>
|
||||
<md-icon class="material-icons">settings_ethernet</md-icon>
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'gateway.update-config' | translate }}
|
||||
</md-tooltip>
|
||||
</md-button>
|
||||
<md-button ng-show="!vm.disabled" ng-disabled="$root.loading"
|
||||
class="md-icon-button md-fab md-mini md-primary"
|
||||
ng-click="vm.removeKeyVal($index)"
|
||||
aria-label="{{ 'gateway.delete' | translate }}">
|
||||
<md-icon class="material-icons">close</md-icon>
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'gateway.delete' | translate }}
|
||||
</md-tooltip>
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
<span ng-show="!vm.kvList.length"
|
||||
layout-align="center center" ng-class="{'disabled': vm.disabled}"
|
||||
class="no-data-found" translate>{{vm.noDataText ? vm.noDataText : 'gateway.no-connectors'}}</span>
|
||||
<div>
|
||||
<md-button ng-show="!vm.disabled" ng-disabled="$root.loading" class="md-raised"
|
||||
ng-click="vm.addKeyVal()"
|
||||
aria-label="{{ 'gateway.add-connectors' | translate }}">
|
||||
<md-tooltip md-direction="top">
|
||||
{{ 'gateway.add-connectors' | translate }}
|
||||
</md-tooltip>
|
||||
<span translate>action.add</span>
|
||||
</md-button>
|
||||
</div>
|
||||
</form >
|
||||
498
ui/src/app/components/gateWay/gateway-form.directive.js
Normal file
498
ui/src/app/components/gateWay/gateway-form.directive.js
Normal file
@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import './gateway-form.scss';
|
||||
/* eslint-disable import/no-unresolved, import/default */
|
||||
|
||||
import gatewayFormTemplate from './gateway-form.tpl.html';
|
||||
|
||||
/* eslint-enable import/no-unresolved, import/default */
|
||||
|
||||
export default angular.module('thingsboard.directives.gatewayForm', [])
|
||||
.directive('tbGatewayForm', GatewayForm)
|
||||
.name;
|
||||
|
||||
/*@ngInject*/
|
||||
function GatewayForm() {
|
||||
return {
|
||||
restrict: "E",
|
||||
scope: true,
|
||||
bindToController: {
|
||||
disabled: '=ngDisabled',
|
||||
keyPlaceholderText: '@?',
|
||||
valuePlaceholderText: '@?',
|
||||
noDataText: '@?',
|
||||
formId: '=',
|
||||
ctx: '=',
|
||||
gatewayFormConfig: '=',
|
||||
theForm: '='
|
||||
},
|
||||
controller: GatewayFormController,
|
||||
controllerAs: 'vm',
|
||||
templateUrl: gatewayFormTemplate
|
||||
};
|
||||
}
|
||||
|
||||
/*@ngInject*/
|
||||
function GatewayFormController($scope, $injector, $document, $mdExpansionPanel, toast, importExport, attributeService, deviceService, userService, $mdDialog, $mdUtil, types, $window, $q) {
|
||||
$scope.$mdExpansionPanel = $mdExpansionPanel;
|
||||
let vm = this;
|
||||
const attributeNameClinet = "current_configuration";
|
||||
const attributeNameServer = "configuration_drafts";
|
||||
const attributeNameShared = "configuration";
|
||||
const attributeNameLogShared = "RemoteLoggingLevel";
|
||||
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"';
|
||||
vm.types = types;
|
||||
|
||||
vm.configurations = {
|
||||
singleSelect: '',
|
||||
host: $document[0].domain,
|
||||
port: 1883,
|
||||
remoteConfiguration: true,
|
||||
accessToken: '',
|
||||
entityType: '',
|
||||
entityId: '',
|
||||
storageType: "memoryStorage", // "memoryStorage"; fileStorage
|
||||
readRecordsCount: 100,
|
||||
maxRecordsCount: 10000,
|
||||
dataFolderPath: './data/',
|
||||
maxFilesCount: 5,
|
||||
securityType: "accessToken", // "accessToken", "tls"
|
||||
caCertPath: '/etc/thingsboard-gateway/ca.pem',
|
||||
privateKeyPath: '/etc/thingsboard-gateway/privateKey.pem',
|
||||
certPath: '/etc/thingsboard-gateway/certificate.pem',
|
||||
connectors: {},
|
||||
remoteLoggingLevel: "DEBUG", // level login
|
||||
remoteLoggingPathToLogs: './logs/'
|
||||
};
|
||||
getGatewaysListByUser(true);
|
||||
|
||||
vm.securityTypes = [{
|
||||
name: 'Access Token',
|
||||
value: 'accessToken'
|
||||
}, {
|
||||
name: 'TLS',
|
||||
value: 'tls'
|
||||
}];
|
||||
|
||||
vm.storageTypes = [{
|
||||
name: 'Memory storage',
|
||||
value: 'memoryStorage'
|
||||
}, {
|
||||
name: 'File storage',
|
||||
value: 'fileStorage'
|
||||
}];
|
||||
|
||||
$scope.$on('gateway-form-resize', function (event, formId) {
|
||||
if (vm.formId == formId) {
|
||||
updateWidgetDisplaying();
|
||||
}
|
||||
});
|
||||
|
||||
function updateWidgetDisplaying() {
|
||||
if (vm.ctx && vm.ctx.$container) {
|
||||
vm.changeAlignment = (vm.ctx.$container[0].offsetWidth <= 425);
|
||||
}
|
||||
}
|
||||
|
||||
updateWidgetDisplaying();
|
||||
|
||||
vm.getAccessToken = (deviceObj) => {
|
||||
if (deviceObj.name) {
|
||||
deviceService.findByName(deviceObj.name, {ignoreErrors: true})
|
||||
.then(
|
||||
function (device) {
|
||||
getDeviceCredential(device.id.id);
|
||||
}
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
function getDeviceCredential(deviceId) {
|
||||
return deviceService.getDeviceCredentials(deviceId).then(
|
||||
(deviceCredentials) => {
|
||||
vm.configurations.accessToken = deviceCredentials.credentialsId;
|
||||
vm.configurations.entityType = deviceCredentials.deviceId.entityType;
|
||||
vm.configurations.entityId = deviceCredentials.deviceId.id;
|
||||
vm.getAttributeStart();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
vm.createDevice = (deviceObj) => {
|
||||
deviceService.findByName(deviceObj.name, {ignoreErrors: true})
|
||||
.then(
|
||||
function (device) {
|
||||
getDeviceCredential(device.id.id).then(() => {
|
||||
getGatewaysListByUser();
|
||||
});
|
||||
},
|
||||
function () {
|
||||
deviceService.saveDevice(deviceObj).then(
|
||||
(device) => {
|
||||
deviceService.getDeviceCredentials(device.id.id).then(
|
||||
(data) => {
|
||||
vm.configurations.accessToken = data.credentialsId;
|
||||
vm.configurations.entityType = device.id.entityType;
|
||||
vm.configurations.entityId = device.id.id;
|
||||
vm.getAttributeStart();
|
||||
getGatewaysListByUser();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
vm.saveAttributeConfig = () => {
|
||||
vm.setAttribute(attributeNameShared, $window.btoa(angular.toJson(vm.getConfigAllByAttributeJSON())), types.attributesScope.shared.value);
|
||||
vm.setAttribute(attributeNameServer, $window.btoa(angular.toJson(vm.getConfigByAttributeTmpJSON())), types.attributesScope.server.value);
|
||||
vm.setAttribute(attributeNameLogShared, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value);
|
||||
};
|
||||
|
||||
vm.getAttributeStart = () => {
|
||||
let initResps = [];
|
||||
vm.configurations.connectors = {};
|
||||
initResps.push(vm.getAttributeConfig(attributeNameClinet, types.attributesScope.client.value));
|
||||
initResps.push(vm.getAttributeConfig(attributeNameServer, types.attributesScope.server.value));
|
||||
initResps.push(vm.getAttributeConfig(attributeNameLogShared, types.attributesScope.shared.value));
|
||||
$q.all(initResps).then((resp) => {
|
||||
vm.getAttributeInitFromClient(resp[0]);
|
||||
vm.getAttributeInitFromServer(resp[1]);
|
||||
vm.getAttributeInitFromShared(resp[2]);
|
||||
}, (err) => {
|
||||
console.log("getAttribute_error", err); //eslint-disable-line
|
||||
});
|
||||
};
|
||||
|
||||
vm.getAttributeConfig = (attributeName, typeValue) => {
|
||||
let keys = [attributeName];
|
||||
return attributeService.getEntityAttributesValues(vm.configurations.entityType, vm.configurations.entityId, typeValue, keys);
|
||||
};
|
||||
|
||||
vm.setAttribute = (attributeName, attributeConfig, typeValue) => {
|
||||
let attributes = [
|
||||
{
|
||||
key: attributeName,
|
||||
value: attributeConfig
|
||||
}
|
||||
];
|
||||
attributeService.saveEntityAttributes(vm.configurations.entityType, vm.configurations.entityId, typeValue, attributes).then(() => {
|
||||
}, (err) => {
|
||||
console.log("setAttribute_", err); //eslint-disable-line
|
||||
});
|
||||
};
|
||||
|
||||
vm.exportConfig = () => {
|
||||
let fileZip = {};
|
||||
fileZip["tb_gateway.yaml"] = vm.getConfig();
|
||||
vm.createConfigByExport(fileZip);
|
||||
vm.getLogsConfigByExport(fileZip);
|
||||
importExport.exportJSZip(fileZip, 'config');
|
||||
vm.setAttribute(attributeNameLogShared, vm.configurations.remoteLoggingLevel.toUpperCase(), types.attributesScope.shared.value);
|
||||
};
|
||||
|
||||
vm.getConfig = () => {
|
||||
let config;
|
||||
config = 'thingsboard:\n';
|
||||
config += ' host: ' + vm.configurations.host + '\n';
|
||||
config += ' remoteConfiguration: ' + vm.configurations.remoteConfiguration + '\n';
|
||||
config += ' port: ' + vm.configurations.port + '\n';
|
||||
config += ' security:\n';
|
||||
if (vm.configurations.securityType === 'accessToken') {
|
||||
config += ' access-token: ' + vm.configurations.accessToken + '\n';
|
||||
} else if (vm.configurations.securityType === 'tls') {
|
||||
config += ' ca_cert: ' + vm.configurations.caCertPath + '\n';
|
||||
config += ' privateKey: ' + vm.configurations.privateKeyPath + '\n';
|
||||
config += ' cert: ' + vm.configurations.certPath + '\n';
|
||||
}
|
||||
config += 'storage:\n';
|
||||
if (vm.configurations.storageType === 'memoryStorage') {
|
||||
config += ' type: memory\n';
|
||||
config += ' read_records_count: ' + vm.configurations.readRecordsCount + '\n';
|
||||
config += ' max_records_count: ' + vm.configurations.maxRecordsCount + '\n';
|
||||
} else if (vm.configurations.storageType === 'fileStorage') {
|
||||
config += ' type: file\n';
|
||||
config += ' data_folder_path: ' + vm.configurations.dataFolderPath + '\n';
|
||||
config += ' max_file_count: ' + vm.configurations.maxFilesCount + '\n';
|
||||
config += ' max_read_records_count: ' + vm.configurations.readRecordsCount + '\n';
|
||||
config += ' max_records_per_file: ' + vm.configurations.maxRecordsCount + '\n';
|
||||
}
|
||||
config += 'connectors:\n';
|
||||
for (let connector in vm.configurations.connectors) {
|
||||
if (vm.configurations.connectors[connector].enabled) {
|
||||
config += ' -\n';
|
||||
config += ' name: ' + connector + ' Connector\n';
|
||||
config += ' type: ' + vm.configurations.connectors[connector].connector + '\n';
|
||||
config += ' configuration: ' + vm.validFileName(connector) + ".json" + '\n';
|
||||
}
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
vm.createConfigByExport = (fileZipAdd) => {
|
||||
for (let connector in vm.configurations.connectors) {
|
||||
if (vm.configurations.connectors[connector].enabled) {
|
||||
fileZipAdd[vm.validFileName(connector) + ".json"] = angular.toJson(vm.configurations.connectors[connector].config);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vm.getLogsConfigByExport = (fileZipAdd) => {
|
||||
fileZipAdd["logs.conf"] = vm.getLogsConfig();
|
||||
};
|
||||
|
||||
vm.getLogsConfig = () => {
|
||||
return vm.remoteLoggingConfig
|
||||
.replace(/{ERROR}/g, vm.configurations.remoteLoggingLevel)
|
||||
.replace(/{.\/logs\/}/g, vm.configurations.remoteLoggingPathToLogs);
|
||||
};
|
||||
|
||||
vm.getConfigAllByAttributeJSON = () => {
|
||||
let thingsBoardAll = {};
|
||||
thingsBoardAll["thingsboard"] = vm.getConfigMainByAttributeJSON();
|
||||
vm.getConfigByAttributeJSON(thingsBoardAll);
|
||||
return thingsBoardAll;
|
||||
};
|
||||
|
||||
vm.getConfigMainByAttributeJSON = () => {
|
||||
let configMain = {};
|
||||
let thingsBoard = {};
|
||||
thingsBoard.host = vm.configurations.host;
|
||||
thingsBoard.remoteConfiguration = vm.configurations.remoteConfiguration;
|
||||
thingsBoard.port = vm.configurations.port;
|
||||
let security = {};
|
||||
if (vm.configurations.securityType === 'accessToken') {
|
||||
security.accessToken = (vm.configurations.accessToken) ? vm.configurations.accessToken : ""
|
||||
} else {
|
||||
security.caCert = vm.configurations.caCertPath;
|
||||
security.privateKey = vm.configurations.privateKeyPath;
|
||||
security.cert = vm.configurations.certPath;
|
||||
}
|
||||
thingsBoard.security = security;
|
||||
configMain.thingsboard = thingsBoard;
|
||||
|
||||
let storage = {};
|
||||
if (vm.configurations.storageType === 'memoryStorage') {
|
||||
storage.type = "memory";
|
||||
storage.read_records_count = vm.configurations.readRecordsCount;
|
||||
storage.max_records_count = vm.configurations.maxRecordsCount;
|
||||
} else if (vm.configurations.storageType === 'fileStorage') {
|
||||
storage.type = "file";
|
||||
storage.data_folder_path = vm.configurations.dataFolderPath;
|
||||
storage.max_file_count = vm.configurations.maxFilesCount;
|
||||
storage.max_read_records_count = vm.configurations.readRecordsCount;
|
||||
storage.max_records_per_file = vm.configurations.maxRecordsCount;
|
||||
}
|
||||
configMain.storage = storage;
|
||||
|
||||
let conn = [];
|
||||
for (let connector in vm.configurations.connectors) {
|
||||
if (vm.configurations.connectors[connector].enabled) {
|
||||
let connect = {};
|
||||
connect.configuration = vm.validFileName(connector) + ".json";
|
||||
connect.name = connector;
|
||||
connect.type = vm.configurations.connectors[connector].connector;
|
||||
conn.push(connect);
|
||||
}
|
||||
}
|
||||
configMain.connectors = conn;
|
||||
|
||||
configMain.logs = $window.btoa(vm.getLogsConfig());
|
||||
|
||||
return configMain;
|
||||
};
|
||||
|
||||
vm.getConfigByAttributeJSON = (thingsBoardBy) => {
|
||||
for (let connector in vm.configurations.connectors) {
|
||||
if (vm.configurations.connectors[connector].enabled) {
|
||||
let typeAr = vm.configurations.connectors[connector].connector;
|
||||
let objTypeAll = [];
|
||||
for (let conn in vm.configurations.connectors) {
|
||||
if (typeAr === vm.configurations.connectors[conn].connector && vm.configurations.connectors[conn].enabled) {
|
||||
let objType = {};
|
||||
objType["name"] = conn;
|
||||
objType["config"] = vm.configurations.connectors[conn].config;
|
||||
objTypeAll.push(objType);
|
||||
}
|
||||
}
|
||||
if (objTypeAll.length > 0) {
|
||||
thingsBoardBy[typeAr] = objTypeAll;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vm.getConfigByAttributeTmpJSON = () => {
|
||||
let connects = {};
|
||||
for (let connector in vm.configurations.connectors) {
|
||||
if (!vm.configurations.connectors[connector].enabled && Object.keys(vm.configurations.connectors[connector].config).length !== 0) {
|
||||
let conn = {};
|
||||
conn["connector"] = vm.configurations.connectors[connector].connector;
|
||||
conn["config"] = vm.configurations.connectors[connector].config;
|
||||
connects[connector] = conn;
|
||||
}
|
||||
}
|
||||
return connects;
|
||||
};
|
||||
|
||||
function getGatewaysListByUser(firstInit) {
|
||||
vm.gateways = [];
|
||||
vm.currentUser = userService.getCurrentUser();
|
||||
if (vm.currentUser.authority === 'TENANT_ADMIN') {
|
||||
deviceService.getTenantDevices({limit: 500}).then(
|
||||
(devices) => {
|
||||
if (devices.data.length > 0) {
|
||||
devices.data.forEach((device) => {
|
||||
if (device.additionalInfo !== null && device.additionalInfo.gateway === true) {
|
||||
vm.gateways.push(device.name);
|
||||
if (firstInit && vm.gateways.length && device.name === vm.gateways[0]) {
|
||||
vm.configurations.singleSelect = vm.gateways[0];
|
||||
let deviceObj = {
|
||||
"name": vm.configurations.singleSelect,
|
||||
"type": "Gateway",
|
||||
"additionalInfo": {
|
||||
"gateway": true
|
||||
}
|
||||
};
|
||||
vm.getAccessToken(deviceObj);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
} else if (vm.currentUser.authority === 'CUSTOMER_USER') {
|
||||
deviceService.getCustomerDevices(vm.currentUser.customerId, {limit: 500}).then(
|
||||
(devices) => {
|
||||
if (devices.data.length > 0) {
|
||||
devices.data.forEach((device) => {
|
||||
if (device.additionalInfo !== null && device.additionalInfo.gateway === true) {
|
||||
vm.gateways.push(device.name);
|
||||
if (firstInit && vm.gateways.length) {
|
||||
vm.configurations.singleSelect = vm.gateways[0];
|
||||
let deviceObj = {
|
||||
"name": vm.configurations.singleSelect,
|
||||
"type": "Gateway",
|
||||
"additionalInfo": {
|
||||
"gateway": true
|
||||
}
|
||||
};
|
||||
vm.getAccessToken(deviceObj);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
vm.getAttributeInitFromClient = (resp) => {
|
||||
if (resp.length > 0) {
|
||||
vm.configurations.connectors = {};
|
||||
let attribute = angular.fromJson($window.atob(resp[0].value));
|
||||
for (var type in attribute) {
|
||||
let keyVal = attribute[type];
|
||||
if (type === "thingsboard") {
|
||||
if (keyVal !== null && Object.keys(keyVal).length > 0) {
|
||||
vm.setConfigMain(keyVal);
|
||||
}
|
||||
} else {
|
||||
for (let typeVal in keyVal) {
|
||||
let typeName = '';
|
||||
if (Object.prototype.hasOwnProperty.call(keyVal[typeVal], 'name')) {
|
||||
typeName = 'name';
|
||||
}
|
||||
let key = "";
|
||||
key = (typeName === "") ? "No name" : ((typeName === 'name') ? keyVal[typeVal].name : keyVal[typeVal][typeName].name);
|
||||
let conn = {};
|
||||
conn["enabled"] = true;
|
||||
conn["connector"] = type;
|
||||
conn["config"] = angular.toJson(keyVal[typeVal].config);
|
||||
vm.configurations.connectors[key] = conn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vm.getAttributeInitFromServer = (resp) => {
|
||||
if (resp.length > 0) {
|
||||
let attribute = angular.fromJson($window.atob(resp[0].value));
|
||||
for (let key in attribute) {
|
||||
let conn = {};
|
||||
conn["enabled"] = false;
|
||||
conn["connector"] = attribute[key].connector;
|
||||
conn["config"] = angular.toJson(attribute[key].config);
|
||||
vm.configurations.connectors[key] = conn;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vm.getAttributeInitFromShared = (resp) => {
|
||||
if (resp.length > 0) {
|
||||
if (vm.types.gatewayLogLevel[resp[0].value.toLowerCase()]) {
|
||||
vm.configurations.remoteLoggingLevel = resp[0].value.toUpperCase();
|
||||
}
|
||||
} else {
|
||||
vm.configurations.remoteLoggingLevel = vm.types.gatewayLogLevel.debug;
|
||||
}
|
||||
};
|
||||
|
||||
vm.setConfigMain = (keyVal) => {
|
||||
if (Object.prototype.hasOwnProperty.call(keyVal, 'thingsboard')) {
|
||||
vm.configurations.host = keyVal.thingsboard.host;
|
||||
vm.configurations.port = keyVal.thingsboard.port;
|
||||
vm.configurations.remoteConfiguration = keyVal.thingsboard.remoteConfiguration;
|
||||
if (Object.prototype.hasOwnProperty.call(keyVal.thingsboard.security, 'accessToken')) {
|
||||
vm.configurations.securityType = 'accessToken';
|
||||
vm.configurations.accessToken = keyVal.thingsboard.security.accessToken;
|
||||
} else {
|
||||
vm.configurations.securityType = 'tls';
|
||||
vm.configurations.caCertPath = keyVal.thingsboard.security.caCert;
|
||||
vm.configurations.privateKeyPath = keyVal.thingsboard.security.private_key;
|
||||
vm.configurations.certPath = keyVal.thingsboard.security.cert;
|
||||
}
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(keyVal, 'storage') && Object.prototype.hasOwnProperty.call(keyVal.storage, 'type')) {
|
||||
if (keyVal.storage.type === 'memory') {
|
||||
vm.configurations.storageType = 'memoryStorage';
|
||||
vm.configurations.readRecordsCount = keyVal.storage.read_records_count;
|
||||
vm.configurations.maxRecordsCount = keyVal.storage.max_records_count;
|
||||
} else if (keyVal.storage.type === 'file') {
|
||||
vm.configurations.storageType = 'fileStorage';
|
||||
vm.configurations.dataFolderPath = keyVal.storage.data_folder_path;
|
||||
vm.configurations.maxFilesCount = keyVal.storage.max_file_count;
|
||||
vm.configurations.readRecordsCount = keyVal.storage.read_records_count;
|
||||
vm.configurations.maxRecordsCount = keyVal.storage.max_records_count;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vm.setSaveTypeConfig = (itemVal) => {
|
||||
vm.configurations.remoteConfiguration = itemVal.item;
|
||||
};
|
||||
|
||||
vm.validFileName = (fileName) => {
|
||||
let fileName1 = fileName.replace("_", "");
|
||||
let fileName2 = fileName1.replace("-", "");
|
||||
let fileName3 = fileName2.replace(/^\s+|\s+$/g, '');
|
||||
let fileName4 = fileName3.toLowerCase();
|
||||
return fileName4;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
40
ui/src/app/components/gateWay/gateway-form.scss
Normal file
40
ui/src/app/components/gateWay/gateway-form.scss
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
.gateway-form{
|
||||
padding: 5px 5px 0;
|
||||
|
||||
.gateway-form-row{
|
||||
md-input-container{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.gateway-config-row-vertical{
|
||||
flex-direction: column;
|
||||
|
||||
.md-select-container{
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-action-buttons{
|
||||
padding-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.security-type {
|
||||
margin-top: 38px;
|
||||
}
|
||||
219
ui/src/app/components/gateWay/gateway-form.tpl.html
Normal file
219
ui/src/app/components/gateWay/gateway-form.tpl.html
Normal file
@ -0,0 +1,219 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2020 The Thingsboard Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<form name="gatewayConfiguration" class="gateway-form">
|
||||
<md-expansion-panel-group>
|
||||
<md-expansion-panel md-component-id="thingsboardPanelId">
|
||||
<md-expansion-panel-collapsed>
|
||||
<div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div>
|
||||
<span flex></span>
|
||||
<md-expansion-panel-icon></md-expansion-panel-icon>
|
||||
</md-expansion-panel-collapsed>
|
||||
<md-expansion-panel-expanded>
|
||||
<md-expansion-panel-header ng-click="$mdExpansionPanel('thingsboardPanelId').collapse()">
|
||||
<div class="tb-panel-title">{{ 'gateway.thingsboard' | translate | uppercase }}</div>
|
||||
<span flex></span>
|
||||
<md-expansion-panel-icon></md-expansion-panel-icon>
|
||||
</md-expansion-panel-header>
|
||||
<md-expansion-panel-content>
|
||||
<tb-gateway-config-select tb-required="true"
|
||||
ng-model="vm.configurations.singleSelect"
|
||||
the-form="gatewayConfiguration"
|
||||
gateway-list="vm.gateways"
|
||||
get_access_token="vm.getAccessToken"
|
||||
create-device="vm.createDevice">
|
||||
</tb-gateway-config-select>
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'gateway.security-type' | translate }}</label>
|
||||
<md-select name="securityType" ng-model="vm.configurations.securityType">
|
||||
<md-option ng-repeat="securityType in vm.securityTypes" ng-value="securityType.value">
|
||||
{{securityType.name}}
|
||||
</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<div layout="row" class="gateway-form-row"
|
||||
ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{ 'gateway.thingsboard-host' | translate }}</label>
|
||||
<input type="text" name="host" ng-model="vm.configurations.host" required>
|
||||
<div ng-messages="gatewayConfiguration.host.$error">
|
||||
<div ng-message="required" translate>extension.field-required</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{ 'gateway.thingsboard-port' | translate }}</label>
|
||||
<input type="number" min="1" max="65535" step="1" name="port"
|
||||
ng-model="vm.configurations.port" required>
|
||||
<div ng-messages="gatewayConfiguration.port.$error">
|
||||
<div ng-message="required" translate>extension.field-required</div>
|
||||
<div ng-message="max" translate>max</div>
|
||||
<div ng-message="min" translate>min</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<div ng-if="vm.configurations.securityType=='tls'">
|
||||
<md-input-container class="md-block security-type">
|
||||
<label>{{'gateway.tls-path-ca-certificate' | translate }}</label>
|
||||
<input type="text" ng-model="vm.configurations.caCertPath" name="caCertPath"/>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'gateway.tls-path-private-key' | translate }}</label>
|
||||
<input type="text" ng-model="vm.configurations.privateKeyPath" name="privateKeyPath"/>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<label>{{'gateway.tls-path-client-certificate' | translate }}</label>
|
||||
<input type="text" ng-model="vm.configurations.certPath" name="certPath"/>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<md-checkbox ng-model="vm.configurations.remoteConfiguration"
|
||||
name="remoteConfiguration"
|
||||
ng-click="vm.setSaveTypeConfig({item: vm.configurations.remoteConfiguration})"
|
||||
aria-label="{{ 'gateway.remote-tip' | translate }}">
|
||||
{{ 'gateway.remote' | translate }}
|
||||
<md-tooltip md-direction="right">{{'gateway.remote-tip' | translate }}</md-tooltip>
|
||||
</md-checkbox>
|
||||
<div layout="row" class="gateway-form-row"
|
||||
ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
|
||||
<md-input-container class="md-block md-select-container" flex>
|
||||
<label>{{'gateway.remote-logging-level' | translate }}</label>
|
||||
<md-select name="loggingLevel" ng-model="vm.configurations.remoteLoggingLevel">
|
||||
<md-option ng-repeat="loggingLevel in vm.types.gatewayLogLevel"
|
||||
ng-value="loggingLevel" ng-selected="$index === 5">
|
||||
{{loggingLevel}}
|
||||
</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{'gateway.remote-logging-path-logs' | translate }}</label>
|
||||
<input type="text" ng-model="vm.configurations.remoteLoggingPathToLogs"
|
||||
name="remoteLoggingPathToLogs" required>
|
||||
<div ng-messages="gatewayConfiguration.remoteLoggingPathToLogs.$error">
|
||||
<div ng-message="required" translate>extension.field-required</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</md-expansion-panel-content>
|
||||
</md-expansion-panel-expanded>
|
||||
</md-expansion-panel>
|
||||
<md-expansion-panel md-component-id="storagePanelId">
|
||||
<md-expansion-panel-collapsed>
|
||||
<div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div>
|
||||
<span flex></span>
|
||||
<md-expansion-panel-icon></md-expansion-panel-icon>
|
||||
</md-expansion-panel-collapsed>
|
||||
<md-expansion-panel-expanded>
|
||||
<md-expansion-panel-header ng-click="$mdExpansionPanel('storagePanelId').collapse()">
|
||||
<div class="tb-panel-title">{{ 'gateway.storage' | translate | uppercase }}</div>
|
||||
<span flex></span>
|
||||
<md-expansion-panel-icon></md-expansion-panel-icon>
|
||||
</md-expansion-panel-header>
|
||||
<md-expansion-panel-content>
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{'gateway.storage-type' | translate }}</label>
|
||||
<md-select required ng-model="vm.configurations.storageType">
|
||||
<md-option ng-repeat="storageType in vm.storageTypes" ng-value="storageType.value">
|
||||
{{storageType.name}}
|
||||
</md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
|
||||
<div layout="row" class="gateway-form-row"
|
||||
ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{'gateway.storage-read-time' | translate }}</label>
|
||||
<input type="number" min="1" name="readRecordsCount"
|
||||
ng-model='vm.configurations.readRecordsCount' required/>
|
||||
<div ng-messages="gatewayConfiguration.readRecordsCount.$error">
|
||||
<div ng-message="required" translate>extension.field-required</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{'gateway.storage-max-time' | translate }}</label>
|
||||
<input type="number" min="1" name="maxRecordsCount"
|
||||
ng-model='vm.configurations.maxRecordsCount' required/>
|
||||
<div ng-messages="gatewayConfiguration.maxRecordsCount.$error">
|
||||
<div ng-message="required" translate>extension.field-required</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
</div>
|
||||
|
||||
<div layout="row" class="gateway-form-row"
|
||||
ng-if="vm.configurations.storageType == 'fileStorage'"
|
||||
ng-class="{'gateway-config-row-vertical': vm.changeAlignment}">
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{'gateway.storage-max-files' | translate }}</label>
|
||||
<input type="number" min="1" name="maxFilesCount" ng-model='vm.configurations.maxFilesCount'
|
||||
required/>
|
||||
<div ng-messages="gatewayConfiguration.maxFilesCount.$error">
|
||||
<div ng-message="required" translate>extension.field-required</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-block" flex>
|
||||
<label>{{'gateway.storage-data-path' | translate }}</label>
|
||||
<input type="text" name="dataFolderPath" ng-model='vm.configurations.dataFolderPath'
|
||||
required/>
|
||||
<div ng-messages="gatewayConfiguration.dataFolderPath.$error">
|
||||
<div ng-message="required" translate>extension.field-required</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</md-expansion-panel-content>
|
||||
</md-expansion-panel-expanded>
|
||||
</md-expansion-panel>
|
||||
<md-expansion-panel md-component-id="connectorsPanelId">
|
||||
<md-expansion-panel-collapsed>
|
||||
<div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div>
|
||||
<span flex></span>
|
||||
<md-expansion-panel-icon></md-expansion-panel-icon>
|
||||
</md-expansion-panel-collapsed>
|
||||
<md-expansion-panel-expanded>
|
||||
<md-expansion-panel-header ng-click="$mdExpansionPanel('connectorsPanelId').collapse()">
|
||||
<div class="tb-panel-title">{{ 'gateway.connectors' | translate | uppercase }}</div>
|
||||
<span flex></span>
|
||||
<md-expansion-panel-icon></md-expansion-panel-icon>
|
||||
</md-expansion-panel-header>
|
||||
<md-expansion-panel-content>
|
||||
<tb-gateway-config
|
||||
gateway-config="vm.configurations.connectors"
|
||||
change-alignment="vm.changeAlignment">
|
||||
</tb-gateway-config>
|
||||
</md-expansion-panel-content>
|
||||
</md-expansion-panel-expanded>
|
||||
</md-expansion-panel>
|
||||
</md-expansion-panel-group>
|
||||
<section layout="row" layout-align="end center" class="form-action-buttons">
|
||||
<md-button class="md-primary md-raised"
|
||||
ng-click="vm.exportConfig()"
|
||||
ng-if="!vm.configurations.remoteConfiguration"
|
||||
ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty"
|
||||
aria-label="{{ 'gateway.download-tip' | translate }}">
|
||||
{{'action.download' | translate }}
|
||||
<md-tooltip>{{'gateway.download-tip' | translate }}</md-tooltip>
|
||||
</md-button>
|
||||
|
||||
<md-button class="md-primary md-raised"
|
||||
ng-click="vm.saveAttributeConfig()"
|
||||
ng-if="vm.configurations.remoteConfiguration"
|
||||
ng-disabled="gatewayConfiguration.$invalid || !gatewayConfiguration.$dirty"
|
||||
aria-label="{{ 'gateway.save-tip' | translate }}">
|
||||
{{'action.save' | translate }}
|
||||
<md-tooltip ng-if="vm.configurations.remoteConfiguration">{{'gateway.save-tip' | translate }}</md-tooltip>
|
||||
</md-button>
|
||||
</section>
|
||||
</form>
|
||||
@ -18,6 +18,7 @@
|
||||
import importDialogTemplate from './import-dialog.tpl.html';
|
||||
import importDialogCSVTemplate from './import-dialog-csv.tpl.html';
|
||||
import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html';
|
||||
import * as JSZip from 'jszip';
|
||||
|
||||
/* eslint-enable import/no-unresolved, import/default */
|
||||
|
||||
@ -28,6 +29,10 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html';
|
||||
export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, $rootScope,
|
||||
dashboardUtils, entityService, dashboardService, ruleChainService, widgetService, toast, attributeService) {
|
||||
|
||||
const JSZIP_TYPE = {
|
||||
mimeType: 'application/zip',
|
||||
extension: 'zip'
|
||||
};
|
||||
|
||||
var service = {
|
||||
exportDashboard: exportDashboard,
|
||||
@ -40,6 +45,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
|
||||
importWidgetType: importWidgetType,
|
||||
exportWidgetsBundle: exportWidgetsBundle,
|
||||
importWidgetsBundle: importWidgetsBundle,
|
||||
exportJSZip: exportJSZip,
|
||||
exportExtension: exportExtension,
|
||||
importExtension: importExtension,
|
||||
importEntities: importEntities,
|
||||
@ -982,6 +988,49 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
|
||||
let dialogElement = element[0].getElementsByTagName('md-dialog');
|
||||
dialogElement[0].style.width = dialogElement[0].offsetWidth + 2 + "px";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param filename
|
||||
* Warn data !!! Not object, if object, then object convert from object to format txt
|
||||
* Example: data = {keyNameFile1: valueFile1,
|
||||
* keyNameFile2: valueFile2...}
|
||||
* fileName - name file of the arhiv
|
||||
*/
|
||||
function exportJSZip(data, filename) {
|
||||
let jsZip = new JSZip();
|
||||
for (let keyName in data) {
|
||||
let valueData = data[keyName];
|
||||
jsZip.file(keyName, valueData);
|
||||
}
|
||||
jsZip.generateAsync({type: "Blob"}).then(function (content) {
|
||||
downloadFile(content, filename, JSZIP_TYPE);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function downloadFile(data, filename, fileType) {
|
||||
console.log("downloadFile", data, filename, fileType); // eslint-disable-line
|
||||
if (!filename) {
|
||||
filename = 'download';
|
||||
}
|
||||
filename += '.' + fileType.extension;
|
||||
var blob = new Blob([data], {type: fileType.mimeType});
|
||||
// FOR IE:
|
||||
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
|
||||
window.navigator.msSaveOrOpenBlob(blob, filename);
|
||||
} else {
|
||||
var e = document.createEvent('MouseEvents'),
|
||||
a = document.createElement('a');
|
||||
a.download = filename;
|
||||
a.href = window.URL.createObjectURL(blob);
|
||||
a.dataset.downloadurl = [fileType.mimeType, a.download, a.href].join(':');
|
||||
e.initEvent('click', true, false, window,
|
||||
0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
a.dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-enable no-undef, angular/window-service, angular/document-service */
|
||||
|
||||
@ -30,6 +30,9 @@ import thingsboardSideMenu from '../components/side-menu.directive';
|
||||
import thingsboardNavTree from '../components/nav-tree.directive';
|
||||
import thingsboardDashboardAutocomplete from '../components/dashboard-autocomplete.directive';
|
||||
import thingsboardKvMap from '../components/kv-map.directive';
|
||||
import thingsboardGatewayConfig from '../components/gateWay/gateway-config.directive';
|
||||
import thingsboardGatewayConfigSelect from '../components/gateWay/gateway-config-select.directive';
|
||||
import thingsboardGatewayForm from '../components/gateWay/gateway-form.directive';
|
||||
import thingsboardJsonObjectEdit from '../components/json-object-edit.directive';
|
||||
import thingsboardJsonContent from '../components/json-content.directive';
|
||||
|
||||
@ -93,6 +96,9 @@ export default angular.module('thingsboard.home', [
|
||||
thingsboardNavTree,
|
||||
thingsboardDashboardAutocomplete,
|
||||
thingsboardKvMap,
|
||||
thingsboardGatewayConfig,
|
||||
thingsboardGatewayConfigSelect,
|
||||
thingsboardGatewayForm,
|
||||
thingsboardJsonObjectEdit,
|
||||
thingsboardJsonContent
|
||||
])
|
||||
|
||||
@ -50,7 +50,8 @@
|
||||
"export": "Export",
|
||||
"share-via": "Share via {{provider}}",
|
||||
"continue": "Continue",
|
||||
"discard-changes": "Discard Changes"
|
||||
"discard-changes": "Discard Changes",
|
||||
"download": "Download"
|
||||
},
|
||||
"aggregation": {
|
||||
"aggregation": "Aggregation",
|
||||
@ -1124,6 +1125,58 @@
|
||||
"function": {
|
||||
"function": "Function"
|
||||
},
|
||||
"gateway": {
|
||||
"key": "Key configuration",
|
||||
"value": "Value configuration",
|
||||
"remove-entry": "Remove configuration",
|
||||
"add-entry": "Add configuration",
|
||||
"no-data": "No configurations",
|
||||
"gateway-required": "Gateway is required.",
|
||||
"gateway-name": "Gateway name",
|
||||
"create-new-gateway": "Create a new gateway",
|
||||
"create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?",
|
||||
"no-gateway-matching": " '{{item}}' not found.",
|
||||
"thingsboard": "ThingsBoard",
|
||||
"connectors": "Connectors configuration",
|
||||
"thingsboard-host": "ThingsBoard Host",
|
||||
"thingsboard-port": "ThingsBoard Port",
|
||||
"security-type": "Security type",
|
||||
"tls-path-ca-certificate": "Path to CA certificate on gateway:",
|
||||
"tls-path-private-key": "Path to private key on gateway:",
|
||||
"tls-path-client-certificate": "Path to client certificate on gateway:",
|
||||
"storage": "Storage",
|
||||
"storage-type": "Storage type",
|
||||
"storage-read-time": "Read records per time:",
|
||||
"storage-max-time": "Maximum records per time:",
|
||||
"storage-max-files": "Maximum files:",
|
||||
"storage-data-path": "Data folder path:",
|
||||
"download-tip": "Download configuration file",
|
||||
"save-tip": "Save configuration file",
|
||||
"remote-tip": "Allow remote configuration",
|
||||
"remote": "Remote configuration",
|
||||
"remote-logging-level": "Logging level",
|
||||
"remote-logging-path-logs": "Path to logs",
|
||||
"connector-type": "Connector type",
|
||||
"update-config": "Add/update config JSON",
|
||||
"delete": "Delete configuration",
|
||||
"title-connectors-json": "Connector {{typeName}} configuration",
|
||||
"json-required": "Config json is required for gateway config.",
|
||||
"json-parse": "Unable to parse config json for gateway config.",
|
||||
"tidy": "Tidy",
|
||||
"tidy-tip": "Tidy config JSON",
|
||||
"transformer-json-config": "JSON for the config*",
|
||||
"toggle-fullscreen": "Toggle fullscreen",
|
||||
"add-connectors": "Add new connectors",
|
||||
"no-connectors": "No connectors",
|
||||
"enabled": "Enabled",
|
||||
"name": "Name",
|
||||
"no-gateway-found": "No gateway found.",
|
||||
"gateway": "Gateway",
|
||||
"keyval-save-err": "Save config error",
|
||||
"keyval-name-err": "Please add <b>Name</b>",
|
||||
"keyval-type-err": "Please add <b>Connector type</b>",
|
||||
"keyval-config-err": "Please add <b>configuration JSON</b>"
|
||||
},
|
||||
"grid": {
|
||||
"delete-item-title": "Are you sure you want to delete this item?",
|
||||
"delete-item-text": "Be careful, after the confirmation this item and all related data will become unrecoverable.",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user