From cc302f18010bf8f2fce940ed8b771ec7d8eba3df Mon Sep 17 00:00:00 2001 From: oleg Date: Thu, 23 Nov 2017 12:54:16 +0200 Subject: [PATCH] add opc-ua --- ui/src/app/common/types.constant.js | 14 + .../extension/extension-dialog.controller.js | 67 ++- .../app/extension/extension-dialog.tpl.html | 26 +- .../extension/extension-table.directive.js | 12 +- .../extension-form-opc.directive.js | 161 +++++ .../extension-form-opc.tpl.html | 550 +++++++++++++++++- .../extensions-forms/extension-form.scss | 16 + ui/src/app/extension/index.js | 2 + ui/src/app/locale/locale.constant.js | 32 +- 9 files changed, 845 insertions(+), 35 deletions(-) create mode 100644 ui/src/app/extension/extensions-forms/extension-form-opc.directive.js diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index dff7635e0a..52c08d21a5 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -332,6 +332,20 @@ export default angular.module('thingsboard.types', []) toDouble: 'extension.to-double', custom: 'extension.custom' }, + extensionOpcSecurityTypes: { + Basic128Rsa15: "Basic128Rsa15", + Basic256: "Basic256", + Basic256Sha256: "Basic256Sha256", + None: "None" + }, + extensionIdentityType: { + anonymous: "anonymous", + username: "username" + }, + extensionKeystoreType: { + PKCS12: "PKCS12", + JKS: "JKS" + }, latestTelemetry: { value: "LATEST_TELEMETRY", name: "attribute.scope-latest-telemetry", diff --git a/ui/src/app/extension/extension-dialog.controller.js b/ui/src/app/extension/extension-dialog.controller.js index 3b13214acd..cc8d7c395f 100644 --- a/ui/src/app/extension/extension-dialog.controller.js +++ b/ui/src/app/extension/extension-dialog.controller.js @@ -29,45 +29,72 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, vm.entityId = entityId; vm.allExtensions = allExtensions; - vm.configuration = {}; - vm.newExtension = {id:"",type:"",configuration:vm.configuration}; - if(!vm.isAdd) { - vm.newExtension = angular.copy(extension); - vm.configuration = vm.newExtension.configuration; - editTransformers(vm.newExtension); + if (extension) { // Editing + //vm.configuration = vm.extension.configuration; + vm.extension = angular.copy(extension); + editTransformers(vm.extension); + } else { // Add new + vm.extension = {}; } - vm.cancel = cancel; - vm.save = save; + vm.extensionTypeChange = function () { + // $scope.theForm.$setPristine(); + // $scope.theForm.$setUntouched(); + + if (vm.extension.type === "HTTP") { + vm.extension.configuration = { + "converterConfigurations": [] + }; + } + if (vm.extension.type === "MQTT") { + vm.extension.configuration = { + "brokers": [] + }; + } + if (vm.extension.type === "OPC UA") { + vm.extension.configuration = { + "servers": [] + }; + } + }; + + vm.cancel = cancel; function cancel() { $mdDialog.cancel(); } + + vm.save = save; function save() { saveTransformers(); if(vm.isAdd) { - vm.allExtensions.push(vm.newExtension); + vm.allExtensions.push(vm.extension); } else { var index = vm.allExtensions.indexOf(extension); if(index > -1) { - vm.allExtensions[index] = vm.newExtension; + vm.allExtensions[index] = vm.extension; } } var editedValue = angular.toJson(vm.allExtensions); - attributeService.saveEntityAttributes(vm.entityType, vm.entityId, types.attributesScope.shared.value, [{key:"configuration", value:editedValue}]).then( - function success() { - $scope.theForm.$setPristine(); - $mdDialog.hide(); - } - ); + attributeService + .saveEntityAttributes( + vm.entityType, + vm.entityId, + types.attributesScope.shared.value, + [{key:"configuration", value:editedValue}] + ) + .then(function success() { + $scope.theForm.$setPristine(); + $mdDialog.hide(); + }); } vm.validateId = function() { var coincidenceArray = vm.allExtensions.filter(function(ext) { - return ext.id == vm.newExtension.id; + return ext.id == vm.extension.id; }); if(coincidenceArray.length) { if(!vm.isAdd) { @@ -82,11 +109,11 @@ export default function ExtensionDialogController($scope, $mdDialog, $translate, } else { $scope.theForm.extensionId.$setValidity('uniqueIdValidation', true); } - } + }; function saveTransformers() { - var config = vm.newExtension.configuration.converterConfigurations; - if(vm.newExtension.type == types.extensionType.http) { + var config = vm.extension.configuration.converterConfigurations; + if(vm.extension.type == types.extensionType.http) { for(let i=0;i - +
@@ -26,8 +26,11 @@
+ + +
@@ -35,41 +38,48 @@
- +
extension.id-required
extension.unique-id-required
+ - + + {{value}} +
extension.type-required
-
+
+
- - +
+ - {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} + {{ 'action.cancel' | translate }}
-
\ No newline at end of file +
+ diff --git a/ui/src/app/extension/extension-table.directive.js b/ui/src/app/extension/extension-table.directive.js index ae28d24fbe..21d716bdb7 100644 --- a/ui/src/app/extension/extension-table.directive.js +++ b/ui/src/app/extension/extension-table.directive.js @@ -126,11 +126,13 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, controllerAs: 'vm', templateUrl: extensionDialogTemplate, parent: angular.element($document[0].body), - locals: { isAdd: isAdd, - allExtensions: vm.allExtensions, - entityId: vm.entityId, - entityType: vm.entityType, - extension: extension}, + locals: { + isAdd: isAdd, + allExtensions: vm.allExtensions, + entityId: vm.entityId, + entityType: vm.entityType, + extension: extension + }, bindToController: true, targetEvent: $event, fullscreen: true, diff --git a/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js b/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js new file mode 100644 index 0000000000..7eeeb29cd8 --- /dev/null +++ b/ui/src/app/extension/extensions-forms/extension-form-opc.directive.js @@ -0,0 +1,161 @@ +/* + * Copyright © 2016-2017 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 'brace/ext/language_tools'; +import 'brace/mode/json'; +import 'brace/theme/github'; + +import './extension-form.scss'; + +/* eslint-disable angular/log */ + +import extensionFormOpcTemplate from './extension-form-opc.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +/*@ngInject*/ +export default function ExtensionFormOpcDirective($compile, $templateCache, $translate, types) { + + + var linker = function(scope, element) { + + + function Server() { + this.applicationName = "Thingsboard OPC-UA client"; + this.applicationUri = ""; + this.host = "localhost"; + this.port = 49320; + this.scanPeriodInSeconds = 10; + this.timeoutInMillis = 5000; + this.security = "Basic128Rsa15"; + this.identity = { + "type": "anonymous" + }; + this.keystore = { + "type": "PKCS12", + "location": "example.pfx", + "password": "secret", + "alias": "gateway", + "keyPassword": "secret" + }; + this.mapping = [] + } + + function Map() { + this.deviceNodePattern = "Channel1\\.Device\\d+$"; + this.deviceNamePattern = "Device ${_System._DeviceId}"; + this.attributes = []; + this.timeseries = []; + } + + function Attribute() { + this.key = "Tag1"; + this.type = "string"; + this.value = "${Tag1}"; + } + + function Timeseries() { + this.key = "Tag2"; + this.type = "long"; + this.value = "${Tag2}"; + } + + + var template = $templateCache.get(extensionFormOpcTemplate); + element.html(template); + + scope.types = types; + scope.theForm = scope.$parent.theForm; + + + if (!scope.configuration.servers.length) { + scope.configuration.servers.push(new Server()); + } + + scope.addServer = function(serversList) { + serversList.push(new Server()); + // scope.addMap(serversList[serversList.length-1].mapping); + + scope.theForm.$setDirty(); + }; + + scope.addMap = function(mappingList) { + mappingList.push(new Map()); + scope.theForm.$setDirty(); + }; + + scope.addNewAttribute = function(attributesList) { + attributesList.push(new Attribute()); + scope.theForm.$setDirty(); + }; + + scope.addNewTimeseries = function(timeseriesList) { + timeseriesList.push(new Timeseries()); + scope.theForm.$setDirty(); + }; + + + scope.removeItem = (item, itemList) => { + var index = itemList.indexOf(item); + if (index > -1) { + itemList.splice(index, 1); + } + scope.theForm.$setDirty(); + }; + + + $compile(element.contents())(scope); + + + scope.fileAdded = function($file, model, options) { + let reader = new FileReader(); + reader.onload = function(event) { + scope.$apply(function() { + if(event.target.result) { + scope.theForm.$setDirty(); + let addedFile = event.target.result; + + if (addedFile && addedFile.length > 0) { + model[options.fileName] = $file.name; + model[options.file] = addedFile.replace(/^data.*base64,/, ""); + + } + } + }); + }; + reader.readAsDataURL($file.file); + + }; + + scope.clearFile = function(model, options) { + scope.theForm.$setDirty(); + + model[options.fileName] = null; + model[options.file] = null; + + }; + + }; + + return { + restrict: "A", + link: linker, + scope: { + configuration: "=", + isAdd: "=" + } + } +} \ No newline at end of file diff --git a/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html b/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html index 779e93ab37..e19984e5d8 100644 --- a/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html +++ b/ui/src/app/extension/extensions-forms/extension-form-opc.tpl.html @@ -15,4 +15,552 @@ limitations under the License. --> -
OPC UA
\ No newline at end of file + + + + extension.configuration + + + + + + + + {{ 'extension.opc-server' | translate }} + + + +
+ extension.opc-add-server-prompt +
+ +
+
    +
  1. + + + + {{ 'action.remove' | translate }} + + + + + + +
    + + + +
    +
    extension.opc-field-required
    +
    +
    + + + + + +
    +
    extension.opc-field-required
    +
    +
    +
    + + +
    + + + +
    +
    extension.opc-field-required
    +
    +
    + + + + +
    +
    extension.opc-field-required
    +
    Port should be in a range from 1 to 65535
    +
    Port should be in a range from 1 to 65535
    +
    +
    +
    + +
    + + + +
    +
    extension.opc-field-required
    +
    +
    + + + + +
    +
    extension.opc-field-required
    +
    +
    +
    + +
    + + + + + + +
    +
    extension.opc-field-required
    +
    +
    + + + + + + +
    +
    extension.opc-field-required
    +
    +
    +
    + + +
    + +
    +
    + + + +
    +
    extension.opc-field-required
    +
    +
    + + + + +
    +
    extension.opc-field-required
    +
    +
    +
    + + + + + + {{ 'extension.opc-keystore' | translate }} + + + + + + + + +
    +
    extension.opc-field-required
    +
    +
    + +
    + + +
    +
    + + + {{ 'action.remove' | translate }} + + close + +
    +
    + + +
    +
    +
    +
    +
    extension.no-file
    +
    {{server.keystore[fieldsToFill.fileName]}}
    +
    + + +
    + + + +
    +
    extension.opc-field-required
    +
    +
    + + + + +
    +
    extension.opc-field-required
    +
    +
    +
    + + + + +
    +
    extension.opc-field-required
    +
    +
    + +
    +
    +
    + + + + + + {{ 'extension.opc-mapping' | translate }} + + +
    +
      +
    1. + + + + {{ 'action.remove' | translate }} + + + + + +
      + + + +
      +
      extension.opc-field-required
      +
      +
      + + + + +
      +
      extension.opc-field-required
      +
      +
      +
      + + + + + + {{ 'extension.opc-mapping-attributes' | translate }} + + +
      +
        +
      1. + + + + {{ 'action.remove' | translate }} + + + + + +
        + + + +
        +
        extension.opc-field-required
        +
        +
        + + + + + {{attrTypeValue | translate}} + + +
        +
        extension.required-type
        +
        +
        +
        + +
        + + + +
        +
        extension.opc-field-required
        +
        +
        + +
        + + +
        +
        +
      2. +
      +
      +
      + + + {{ 'extension.add-map' | translate }} + + add + action.add + +
      +
      +
      +
      + + + + + {{ 'extension.opc-timeseries' | translate }} + + +
      +
        +
      1. + + + + {{ 'action.remove' | translate }} + + + + +
        + + + +
        +
        extension.opc-field-required
        +
        +
        + + + + + {{attrTypeValue | translate}} + + +
        +
        extension.opc-field-required
        +
        +
        +
        +
        + + + +
        +
        extension.required-value
        +
        +
        +
        +
        +
        +
      2. +
      +
      +
      + + + {{ 'extension.add-timeseries' | translate }} + + add + action.add + +
      +
      +
      +
      + + +
      +
      +
    2. +
    +
    +
    + + + {{ 'extension.add-map' | translate }} + + add + action.add + +
    +
    +
    +
    + +
    +
    +
  2. +
+ +
+ + add + extension.opc-add-another-server + +
+ +
+
+
+
+ +
+
\ No newline at end of file diff --git a/ui/src/app/extension/extensions-forms/extension-form.scss b/ui/src/app/extension/extensions-forms/extension-form.scss index cfea19f418..142516c56c 100644 --- a/ui/src/app/extension/extensions-forms/extension-form.scss +++ b/ui/src/app/extension/extensions-forms/extension-form.scss @@ -37,4 +37,20 @@ .ace_text-input { position:absolute!important } +} + +.extensionDialog { + min-width: 1000px; +} + +.tb-container-for-select { + height: 58px; +} + +.tb-drop-file-input-hide { + height: 200%; + display: block; + position: absolute; + bottom: 0; + width: 100%; } \ No newline at end of file diff --git a/ui/src/app/extension/index.js b/ui/src/app/extension/index.js index 78fd56d824..4c9f812787 100644 --- a/ui/src/app/extension/index.js +++ b/ui/src/app/extension/index.js @@ -16,10 +16,12 @@ import ExtensionTableDirective from './extension-table.directive'; import ExtensionFormHttpDirective from './extensions-forms/extension-form-http.directive'; +import ExtensionFormOpcDirective from './extensions-forms/extension-form-opc.directive'; import {ParseToNull} from './extension-dialog.controller'; export default angular.module('thingsboard.extension', []) .directive('tbExtensionTable', ExtensionTableDirective) .directive('tbExtensionFormHttp', ExtensionFormHttpDirective) + .directive('tbExtensionFormOpc', ExtensionFormOpcDirective) .directive('parseToNull', ParseToNull) .name; \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index d8b861f7ed..6f626b5ed8 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -773,14 +773,44 @@ export default angular.module('thingsboard.locale', []) "json-parse": "Unable to parse transformer json.", "attributes": "Attributes", "add-attribute": "Add attribute", + "add-map": "Add mapping element", "timeseries": "Timeseries", "add-timeseries": "Add timeseries", + + "opc-field-required": "Field is required", + "opc-server": "Servers", + "opc-add-server-hint": "Add server", + "opc-add-server-prompt": "Please add server", + "opc-server-id": "Server id", + "opc-timeseries": "Timeseries", + "opc-application-name": "Application name", + "opc-application-uri": "Application uri", + "opc-host": "Host", + "opc-port": "Port", + "opc-scan-period-in-seconds": "Scan period in seconds", + "opc-timeout-in-millis": "Timeout in milliseconds", + "opc-security": "Security", + "opc-identity": "Identity", + "opc-keystore": "Keystore", + "opc-type": "Type", + "opc-keystore-type":"Type", + "opc-keystore-location":"Location *", + "opc-keystore-password":"Password", + "opc-keystore-alias":"Alias", + "opc-keystore-key-password":"Key password", + "opc-mapping":"Mapping", + "opc-device-node-pattern":"Device node pattern", + "opc-device-name-pattern":"Device name pattern", + "opc-mapping-attributes":"Mapping attributes", + "opc-username":"Username", + "opc-password":"Password", + "opc-add-another-server":"Add another server", }, "fullscreen": { "expand": "Expand to fullscreen", "exit": "Exit fullscreen", "toggle": "Toggle fullscreen mode", - "fullscreen": "Fullscreen" + "fullscreen": "Fullscreen", }, "function": { "function": "Function"