Browse Source
* 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>pull/2380/head
committed by
GitHub
16 changed files with 1712 additions and 3 deletions
@ -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> |
|||
@ -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 */ |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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> |
|||
@ -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}); |
|||
}; |
|||
} |
|||
|
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 > |
|||
@ -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; |
|||
}; |
|||
} |
|||
|
|||
|
|||
@ -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; |
|||
} |
|||
@ -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> |
|||
Loading…
Reference in new issue